SWT Link flickers with gradient background - java

I'm developing a an eclipse plugin that uses an SWT interface. I need to display text, and within that text there needs to be links. The only two widgets that I've found that will allow me to include clickable links in text are Link and Browser. Browser, however, is overkill for my needs, and I couldn't properly customize the look of it. This only leaves the Link widget.
The problem is I need the Link widget to inherit a gradient from the Composite in which it is in. It does this correctly, only when it is resized or scrolled the Link component flickers. The Link is the only component in which I have seen this effect.
In an attempt to fix this I've tried manipulating other components into having clickable links, but I haven't found a good solution yet.
Is there anyway to fix the flickering effect on the Link, or is there a different component which would support links?
Thanks,
Brian

After spending the day working on this, I came up with a workaround. I created a Composite for the text area. For each word that isn't part of a url,got its own label. For links, each letter got its own label. Then the labels for the url characters got a listener to launch a browser. Using this method provided the Link functionality, handled resizing properly, and has no flicker.

Have you tried passing SWT.NO_BACKGROUND to your Link widget? It might get a little strange... and you may have to do a little more work to get the gui drawing properly, but that would be my first guess.
Other than that, here's my Quick n' dirty implementation of a link inside of a StyledText. You will need to fill in for changing the cursor (if that's something you want), as well as coming up with a good "text to link" mapping scheme.
The only thing is I'm not sure if StyledText will inherit your background... give it a shot.
public class StyledTextExample {
public static void main(String [] args) {
// create the widget's shell
Shell shell = new Shell();
shell.setLayout(new FillLayout());
shell.setSize(200, 100);
Display display = shell.getDisplay();
// create the styled text widget
final StyledText widget = new StyledText(shell, SWT.NONE);
String text = "This is the StyledText widget.";
widget.setText(text);
widget.setEditable(false);
final StyleRange hyperlinkStyle = new StyleRange();
String linkWord = "StyledText";
hyperlinkStyle.start = text.indexOf(linkWord);
hyperlinkStyle.length = linkWord.length();
hyperlinkStyle.fontStyle = SWT.BOLD;
hyperlinkStyle.foreground = display.getSystemColor(SWT.COLOR_BLUE);
widget.setStyleRange(hyperlinkStyle);
widget.addMouseListener(new MouseAdapter() {
public void mouseUp(MouseEvent arg0) {
Point clickPoint = new Point(arg0.x, arg0.y);
try {
int offset = widget.getOffsetAtLocation(clickPoint);
if (widget.getStyleRangeAtOffset(offset) != null) {
System.out.println("link");
}
} catch (IllegalArgumentException e) {
//ignore, clicked out of text range.
}
}});
shell.open();
while (!shell.isDisposed())
if (!display.readAndDispatch()) display.sleep();
}
}

Related

Java application - swing - pop up on right click changes background colour

I am having issues with my java application and I can't find a suitable reply.
In summary a right click triggered default pop up menu changes my chart's background color behind the pop up.
You can find images below. I am happy to keep the default popup without the "buggy" behaviour or develop my own if required.
A click of a button starts a stream of data and adds a chart to a JInternalFrame component:
If I right click on the image a default pop up comes up:
If I then click away the rectangle area covered by the popup will overlay the chart like this:
TimeseriesMonitorModel model = new DefaultTweetMonitorModel();
jif.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
try {
jif.setContentPane(new TweetSeriesChartPane(model, TweetMonitor.keywords, tkc));
jif.setSize(jif.getWidth(), jif.getHeight());
} catch (InterruptedException ex) {
Logger.getLogger(TweetMonitor.class.getName()).log(Level.SEVERE, null, ex);
}
jif.setVisible(true);
where jif is the Jinternalframe and
public TweetSeriesChartPane(TimeseriesMonitorModel model, String[] seriesNames, TweetKeywordCount tkc) throws InterruptedException {
this.seriesNames = seriesNames;
this.tkc = tkc;
this.model = model;
XYChartTimeseries myRealTimeChart = new XYChartTimeseries();
chart = myRealTimeChart.getChartWithTitle();
List[] tweetData = model.getFrequencyCount(new AtomicIntegerArray(seriesNames.length)); // we are starting from 0
int i = 0;
for (String keyword : seriesNames) {
List<Integer> yData = (List<Integer>) tweetData[1].get(i);
chart.addSeries(keyword, tweetData[0], yData); // adding first value
i++;
}
setLayout(new BorderLayout());
XChartPanel<XYChart> chartPane = new XChartPanel<>(chart);
add(chartPane);
UpdateWorker worker = new UpdateWorker(this, seriesNames, this.tkc);
worker.execute();
}
I've managed to temporary solve the above.
I've checked specifically this line of code
XChartPanel<XYChart> chartPane = new XChartPanel<>(chart);
which extends Chart and Jpanel. The code is from the knowm-chart dependency https://github.com/knowm/XChart/blob/develop/xchart/src/main/java/org/knowm/xchart/XChartPanel.java)
Apparently it adds a listener and the customized PopUpMenu. After reviewing it, it looks like it didn't do any repainting when the mouse was clicked outside the PopUpMenu area.
So I created a new class and tried to customise it. However, the repaint was flickering the screen and I couldn't get it to work only in the PopUpMenu area.
I ended up disabling the .addMouseListener call so now I don't get any popUpMenu. I sad compromise, but oh well.
BTW:
Thanks to both, regardless of the last unneeded comment which didn't add any value.
I did read the link and I though I provided enough information.
In any case, posting to code helped me troubleshoot it

Nez / Monogame : Can't click buttons but mouse actions are properly registering

So, I dont know if I am missing something but any way I work it or look into it I am unable to click on any buttons on the screens.
I made a dialog box with a button essentially as shown in the sample projects but when ever I try to click the button nothing happens at all.
I tried firing the onClick programmatically and it works fine, I also tested that the mouse inputs were being registered okay and they were.
I am at a total loss for any reason why this wouldnt work.
My class code is below:
public class Interactable : UICanvas
{
public void interact()
{
var skin = Skin.createDefaultSkin();
var table = stage.addElement(new Table());
table.center();
table.setFillParent(true);
table.add(talk(this.entity.name, "Stay a while and glisten", "Bye!"));
}
public Dialog talk(string title, string messageText, string closeButtonText)
{
var skin = Skin.createDefaultSkin();
var style = new WindowStyle
{
background = new PrimitiveDrawable(new Color(50, 50, 50)),
//Dims the background
stageBackground = new PrimitiveDrawable(new Color(0, 0, 0, 150))
};
var dialog = new Dialog(title, style);
dialog.getTitleLabel().getStyle().background = new PrimitiveDrawable(new Color(55, 100, 100));
dialog.pad(20, 5, 5, 5);
dialog.addText(messageText);
var exitButton = new TextButton(closeButtonText, skin);
exitButton.onClicked += butt => dialog.hide();
dialog.add(exitButton);
return dialog;
}
}
}
The interact() is called when running up to another entity and pressing "E".
Which causes everything to render properly but I can't click on the button.
Additionally:
When i try to view exitButton co-ordinates theyre always 0 no matter what although the dialog appears in the middle of the window
Monogame version: 3.7
Nez version: 0.9.2
UPDATE:
So it seems like buttons are clickable but their click box is not even nearly aligning with where the buttons are truely rendered.
UPDATE2:
It seems that the issue is that where the button is being rendered and where the actual click box is are not the same.
I have Zoomed in the camera by 2 and the camera also follows my little character around. The dialog will then appear at the X,Y in relation to the current camera view but the actual click box appears at the X,Y in terms of the TiledMap (which is not always on screen).
Not too sure how to work around this.
So! The issue I was having was I was using one renderer for the entire this (The RenderLayerRenderer.) What I have done to fix this is start another renderer (A ScreenSpaceRenderer). This allows me to use it to render UI and its XY variables do not change but are just static to the visual area.
So i ended up with two renders like so:
addRenderer(new RenderLayerRenderer(0,new int[] { (int)RenderLayerIds.First, (int)RenderLayerIds.Second, (int)RenderLayerIds.Third}));
addRenderer(new ScreenSpaceRenderer(1, new int[] { (int)RenderLayerIds.UILayer }));
Using the first for my game rendering and the bottom on just for HUD things!

SWT: How to get a Composite to automatically fill it's container

In the following code I use SWT and also draw2d. When I run the program however nothing shows up. After some debugging I've found it to be because the container's size are all 0. I have to manually enter a size like
canvasData.heightHint = 500;
canvasData.widthHint = 500;
This is fine and dandy but I need these containers to fit themselves into the space given to them by form. Is there any way to accomplish this? Any advice would be helpful.
/* (non-Javadoc)
* #see org.eclipse.ui.forms.editor.FormPage#createFormContent(org.eclipse.ui.forms.IManagedForm)
*/
#Override
protected void createFormContent( IManagedForm managedForm )
{
super.createFormContent(managedForm);
ScrolledForm form = managedForm.getForm();
Composite tabParent = managedForm.getToolkit().createComposite(form.getBody());
GridData tabParentData = new GridData(SWT.FILL,SWT.FILL,true,true);
tabParent.setLayoutData(tabParentData);
tabParent.setLayout(new GridLayout());
FigureCanvas canvas = new FigureCanvas(tabParent);
GridData canvasData = new GridData(SWT.FILL,SWT.FILL,true,true);
canvas.setLayoutData(canvasData);
//diagramContents is an IFigure from draw2d. it holds other IFigures, Labels, ect.
//Essentially everything being displayed
canvas.setContents(diagramContents);
//This method is what creates everything being displayed (the stuff in diagramContents)
refreshDisplayFromModel();
canvas.pack();
tabParent.pack();
}
Throw out GridLayout and GridData and try to set a FillLayout onto tabParent like this:
form.getBody().setLayout(new FillLayout()); //*
Composite tabParent = managedForm.getToolkit().createComposite(form.getBody());
tabParent.setLayout(new FillLayout());
FigureCanvas canvas = new FigureCanvas(tabParent);
//no layouting
canvas.setContents(diagramContents);
FillLayout doesn't need a constraints object and just makes its child grab everything. If you have more than one child, I propose you take a look at RowLayout for simple stuff and FormLayout for complex stuff, and just forget about GridLayout.
You can get rid of the pack() calls aswell.
*: Since you gave tabParent a GridData object aswell, I figured your form has a GridLayout. Got rid of that for you, too.
Use
canvas.getViewPort().setContentsTracksWidth( true );
canvas.getViewPort().setContentsTracksHeight( true );

No vertical scroll in browser

I'm developing a Vaadin application and am having extreme difficulty getting some aspects of the layout as I want. The major problem right now is that I can't seem to get a vertical scroll in my layout no matter how big the size of the content is or how small the browser window is..
I have read up on the subject, I know that the hLayout and the vLayout doesn't support scrollbars but the Panel do. I've tried in many different combinations to make it work but I've only managed to get a horizontal scrollbar to generate but never a vertical one.
Another problem is that I'm building the application inside an existing "template" provided by the company. This template contains a footer containing some copyright information. This footer doesn't seem to occupy any space in the browser window with regards to the content I'm adding, which causes when viewing on smaller screens the horizontal scrollbar to appear "underneath" the footer, non-accessible... I'll provide some of the code of how it looks now.
public class InventorySimCardTable extends M2MViewBase { //M2MViewBase extends VerticalLayout
private final SPanel mainContent = Cf.panel("");
private final SPanel tabPanel = Cf.panel("");
private final SVerticalLayout tabcontent = Cf.vLayout();
protected InventoryFilterPanel inventoryFilterPanel;
#Override
protected void initComponent() {
setSizeFull();
tabPanel.setSizeFull();
tabPanel.getContent().setSizeUndefined();
Table simCardTable = new Table();
simCardTable.setWidth("1898px");
simCardTable.setPageLength(15);
tableContainer.setSizeUndefined();
tableContainer.addComponent(simCardTable);
mainContent.setWidth("99%");
mainContent.setHeight("100%");
mainContent.setContent(tableContainer);
mainContent.setScrollable(true);
centeringlayout.setSizeFull();
centeringlayout.addComponent(mainContent);
centeringlayout.setComponentAlignment(mainContent, Alignment.MIDDLE_CENTER);
tabPanel.addComponent(centeringlayout);
addComponent(tabPanel);
}
}
I would love to know if anyone sees any obvious errors in my code. And if anyone knows what property I can set on the footer CSS to have it occupy space in the content view so that the horizontal scroll doesn't appear underneath it. Thank you!
What I did to solve this issue was to structure the code as follows. This will create a vertical and horizontal scroll bar for the Panel holding my filter component and the table. Hopefully this can help someone with a similar problem.
#Override
protected void initComponent() {
super.initComponent();
if(!tableCreated) {
createSimCardsTable();
tableCreated = true;
}
mainWindow = this.getWindow();
Panel basePanel = new Panel("");
basePanel.addComponent(inventoryFilterPanel);
AbstractComponent separatorLine = Cf.horizontalLine(); //Of no signficance
separatorLine.addStyleName("m2m-horizontal-line-list-separator");
separatorLine.setWidth("99%");
basePanel.addComponent(separatorLine);
basePanel.addComponent(simCardTable);
basePanel.setSizeFull();
basePanel.getContent().setSizeUndefined(); // <-- This is the important part
addComponent(basePanel);
setExpandRatio(basePanel, 1);
}
All Vaadin components have size undefined by default, so usually there is no need to call method setSizeUndefined(). Also there is no need to call setScrollable(true), because it enables only programmatic scrolling possibility.
When I was trying to make a sense of scrolling appearance I wrote a simple skeleton of layout. Try this out as a content of the main window:
import com.vaadin.ui.HorizontalSplitPanel;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;
public class Skeleton extends VerticalLayout {
public Skeleton() {
setSizeFull();
addComponent(new Label("Header component"));
HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
Panel leftComponent = new Panel();
Panel rightComponent = new Panel();
splitPanel.setFirstComponent(leftComponent);
splitPanel.setSecondComponent(rightComponent);
for (int i = 0 ; i < 200 ; i ++) {
leftComponent.addComponent(new Label("left"));
rightComponent.addComponent(new Label("right"));
}
leftComponent.setSizeFull();
rightComponent.setSizeFull();
addComponent(splitPanel);
setExpandRatio(splitPanel, 1);
addComponent(new Label("Footer component"));
}
}
You should see scrollbars inside the nested panels. But if setSizeFull() is removed from Skeleton layout, then it is not limited in size (by default) and grows downwards - then only the scrollbar of the whole window appears.
Add this to your styles.css
.v-verticallayout > div {
overflow-y: auto ! important;
}
First of all try to make your panel scrollable by calling setScrollable(true) method, but this will not work if you set some custom layout with setSizeFull() as this panel new layout.
If you exactly know that you application will be opened in device with small screen resolution, you simple can set for your "primary"/"main" layout some fixed width and height, or add some CSS style with params like min-width: {some value} px, min-height: {some value} px.
Based on this post, I added vertical.setSizeUndefined(); and started seeing vertical scrollbars.
setMainWindow(new Window(title ));
vertical.setSizeFull();
vertical.setHeight("100%");
toolbar = createToolbar();
vertical.addComponent(toolbar);
vertical.setExpandRatio(toolbar, 0.03f);
Component tree = buildTree();
vertical.addComponent(tree);
vertical.setExpandRatio(tree, 0.97f);
vertical.setSizeUndefined();
getMainWindow().setContent(vertical);>
The only way I could fix this now (v6.8.14) is to specify the height in px values in stead of %
Use CustomLayout, always. It's faster, more efficient and by controlling html and css easily you can acieve a graphically consistent result

JLabel not greyed out when disabled, when HTML text displayed

How do I get a JLabel displaying a HTML string to appear greyed out (which is the behaviour of JLabels that don't display HTML text)? Is there another way than actually changing the colour myself by modifying the foreground property?
JLabel label1 = new JLabel("Normal text");
JLabel label2 = new JLabel("<html>HTML <b>text</b>");
// Both labels are now black in colour
label1.setEnabled(false);
label2.setEnabled(false);
// label1 is greyed out, label2 is still black in colour
Thank you very much for all of your responses. From what I gather, it seems that Java doesn't support automatic greying out of JLabels when they use HTML text. Suraj's solution has come closest to the fix considering the limitations.
I have however, tried a different out-of-the box approach, where I have put the HTML text JLabels inside of an inner JPanel and did this:
mInnerPanel.setEnabled(shouldShow); //shouldShow is a boolean value
Which hasn't worked. Any suggestions for this way?
EDIT: Added implemented solution.
If text is HTML, the text wont be grayed out because of the following code in BasicLabelUI#paint()
View v = (View) c.getClientProperty(BasicHTML.propertyKey);
if (v != null) {
v.paint(g, paintTextR);
}
As you can see if the text is html, then the View is used to paint and it is not checked wheter the label is enabled or not.
Hence we need to do it explictly as shown below:
label2.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (!evt.getPropertyName().equals("enabled"))
return;
if (evt.getNewValue().equals(Boolean.FALSE))
label2.setText("<html><font color=gray>HTML <b>text</b></html>");
else
label2.setText("<html><font color=black>HTML <b>text</b></html>");
}
});
Implemented solution:
Color foreground = (shouldShow) ? SystemColor.textText : SystemColor.textInactiveText;
for (Component comp : mInnerPanel.getComponents())
{
comp.setForeground(foreground);
}
Caved in and used setForeground in the end, as it appears that Java seems to explicitly ignore the enabled property when painting JLabels so long as it contains HTML text. See also #Suraj's answer, for "pure" solution.
I would suggest the following, which is combination of two solutions provided here:
public class HtmlLabel extends JLabel{
public void setEnabled(boolean enabled){
if(getClientProperty(BasicHTML.propertyKey) != null ){
Color foreground = (enabled) ? SystemColor.textText : SystemColor.textInactiveText;
setForeground(foreground);
}
super.setEnabled(enabled);
}
}
You can specify the font color in the HTML.
Override the paint method in the UI, set the client property BasicHTML.propertyKey to null if it is disabled and call super...

Categories

Resources