How to dispose of an SWT Font set on a Draw2D Label? - java

In a GEF editor, I have the following EditPart:
public class MyLabelEditPart extends AbstractGraphicalEditPart {
#Override
protected IFigure createFigure() {
return new Label();
}
#Override
protected void refreshVisuals() {
MyModel model = (MyModel) getModel();
Label figure = (Label) getFigure();
EditPart parent = getParent();
Font font = new Font(Display.getCurrent(), "sansserif", 11, SWT.BOLD);
figure.setFont(font);
figure.setForegroundColor(ColorConstants.darkGray);
figure.setText(model.getValueString());
parent.refresh();
}
All works fine with most models, but - you will have spotted the error already - I never dispose of the font. So, with a large-ish model of 10k+ tokens, this throws an org.eclipse.swt.SWTError: No more handles. At least I think (hope) this is what causes the error.
Now I cannot figure out how to dispose the font, as the figure for the EditPart is a Draw2D Label, not an SWT Widget. How can I make sure the dreaded error can be circumvented?

Apart from Baz' solution to make the font a static field, a good solution is to use a JFace FontRegistry, as detailed in this strangeoptics blog post.

If you want to keep and reuse all your resources ( images, fonts, colors..etc) at one point rather than using registries, follow this windows builder class
http://code.google.com/p/goclipse/source/browse/trunk/goclipse-n/src/org/eclipse/wb/swt/SWTResourceManager.java?r=445

Related

Xamarin.Forms app crashes when selecting text from custom label on Android

Iv'e created an app in Xamarin.Forms and then created a custom lable with a custom renderer for android. The renderer's job is to make the label text selectable. And it works, the label text is now selectable. The problem is, while it works perfectly with regular lables, when i try to select text in a label which has both Hebrew and English (RTL and LTR) text, the app crashes and I get this error:
Java.Lang.ArrayIndexOutOfBoundsException: 'length=35; index=35' (indexes and length vary between crashes but are always equal to each other).
I looked online for a solution and couldn't find one... I also tried other implementations of the renderer and they all had the same error. (I think it might be a problem with Android/Xamarin)
Can someone help me?
Code for renderer:
class CustomLabelRenderer : LabelRenderer
{
public CustomLabelRenderer(Context context) : base(context) {}
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
Control.SetTextIsSelectable(true);
}
}
(Custom label (SELabel) has no code, it just inherits from the regular Label)
Device Logs:
Log.txt
I use google translation "Welcome to Xamarin.Forms" to Hebrew "ברוך הבא ל - Xamarin.Forms.". And make a code sample to test on my side. It works well. Please check my code.
SELabel:
public class SELabel:Label
{
}
CustomLabelRenderer:
[assembly: ExportRenderer(typeof(SELabel), typeof(CustomLabelRenderer))]
namespace SelectableLabel.Droid
{
public class CustomLabelRenderer : LabelRenderer
{
public CustomLabelRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
Control.SetTextIsSelectable(true);
}
}
}
Usage:
<StackLayout>
<!-- Place new controls here -->
<local:SELabel
HorizontalOptions="Center"
Text="Welcome to Xamarin.Forms! Hebrew: ברוך הבא ל - Xamarin.Forms."
VerticalOptions="CenterAndExpand" />
</StackLayout>
Screenshot:
You could download the source file from the GitHub for reference.
https://github.com/WendyZang/Test/tree/master/SelectableLabel

Add style to CompletionProposal in Eclipse RCP (Content assist)

I'm creating a custom content assist for an editor, this is how I'm creating the proposals:
#Override
public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
String test = "Test";
ContextInformation contextInfo = new ContextInformation("Context display test", "information display test");
CompletionProposal proposal = new CompletionProposal(test,
offset,
0,
test.length(),
Activator.getImage("icons/sample.png"),
test,
contextInfo,
"Additional info");
return new ICompletionProposal[] {proposal};
}
This is the result:
Which is fine, but for example, in the content assist of the Java editor, they are using colors such as blue and gray:
I know there is a class called StyledText that could help but I can't find a good example to use it in combination with CompletionProposal.
The extension interface ICompletionProposalExtension6 supports styled display strings. Its sole method getStyledDisplayString() must return a StyledString that is used for display.
Instead of creating an instance of CompletionProposal you would have to implement your own ICompletionProposal that also implements the above mentioned extension. For example:
class StyledCompletionProposal
implements ICompletionProposal, ICompletionProposalExtension6
{
...
#Override
public StyledString getStyledDisplayString() {
return new StyledString("test").append(" [10%]", Styler.QUALIFIER_STYLER);
}
}
In addition, the content assistant must be configured to enable coloured labels. For editors, this is usually done in SourceViewerConfiguration::getContentAssistant:
ContentAssistant contentAssistant = new ContentAssistant();
contentAssistant.enableColoredLabels(true);

GXT - Can't reach onscroll event handler on text area

I'm trying to have an handler on Text Area(gxt) to get to know when user reach the top of text area.
TextArea logTextArea = new TextArea();
logTextArea.setReadOnly(true);
logTextArea.addDomHandler(new ScrollHandler() {
#Override
public void onScroll(ScrollEvent event) {
InputElement textAreaElement = logTextArea.getCell().getInputElement(logTextArea.getElement());
int scrollTop = textAreaElement.getScrollTop();
}
}, ScrollEvent.getType());
VerticalLayoutContainer dataContainer = new VerticalLayoutContainer();
HorizontalLayoutContainer secondRow = new HorizontalLayoutContainer();
secondRow.add(logTextArea, new HorizontalLayoutData(1, 1, new Margins(5, 10, 5, 10)));
dataContainer.add(secondRow, new VerticalLayoutData(1, 0.5));
add(dataContainer);//this class extends ContentPanel
This handler is never called on scroll, but I also tried with a lot of other events, like mouseover, mousewhell, mouseclick ... and all of these events worked. Can somebody help with any idea?
AFAIK, scroll event will not work in cell based widgets out of the box if the event's target and the widget are not the same elements.
And GXT's TextArea is a widget that has such DOM structure.
That's all because scroll event is a "non bubbling" event.
AFAIK, GWT widgets, that uses cells, have a special handling for non bubbling events to be dispatched through GWT events system.
And the list of types of supporting non bubbling events are too short and limited to focus, blur, load and error events.
See CellBasedWidgetImplStandard class for details.
First solution, that I may suggest, is to explicitly assign onscroll handler for textarea.
For example:
Event.sinkEvents(textAreaElement, Event.getEventsSunk(textAreaElement) | Event.ONSCROLL);
here textAreaElement - is the textarea DOM element.
But this should be done every time when the cell rerenders its content (e.g. when setValue method called for TextArea widget).
Other solution is a bit hacky and uses private api implementation, but could be applied only once to cell based widget. You can do the same things as done in CellBasedWidgetImplStandard.sinkEvent(Widget, String) e.g. :
WidgetHelper.sinkEvent(logTextArea.getElement(), BrowserEvents.SCROLL);
where WidgetHelper may looks like this:
public class WidgetHelper {
public static void sincEvent(Element element, String typeName){
element.setAttribute("__gwtCellBasedWidgetImplDispatching" + typeName, "true");
sinkEvent0(element, typeName);
}
private static native void sinkEvent0(Element element, String typeName) /*-{
element.addEventListener(typeName,
#com.google.gwt.user.cellview.client.CellBasedWidgetImplStandard::dispatchNonBubblingEvent, true);
}-*/;
}
Probably, this is a subject to create an issue in GWT project.

Clear prompt text in JavaFX TextField only when user starts typing

The default behaviour is that the prompt text in the field is erased as the field is being focused. That is when the marker is in the field.
Is it possible to configure the textfield so the prompt text is only erased when the user have started typing?
Otherwise I need to add a label beside/over each textfield for description of the value in it.
I know it's a bit old, but I needed it myself and this is still very relevant.
I will complete jewelsea's answer and give a simpler solution.
Background
Apparently this was the default behavior of Java(FX) (prompt text in a TextField was cleared only when the user starts typing).
But then, following a request (or a bug report) in the JIRA system,
Java changed this behavior (and made the default to clear the text when the TextField gets focus).
You can review this bug report here.
Solution
To get back to the old default (and nicer behavior IMO), all you need to do is to add the following line(s) of code.
In case your application interface is written using proper Java code.
Java Code:
textField.setStyle("-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);");
Where textField is your TextField component.
And in case your application interface is written using FXML and CSS, add the following to your CSS file.
JavaFX FXML (CSS):
.text-input, .text-input:focused {
-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);
}
--------------------------------------------------------------------------------
Cleared-on-Focus Behavior
Currently it's the default behavior (the text-field's prompt text is cleared when the text-field gets focus),
so you don't need to do anything to get this behavior,
but in case Java will decide to go back to the cleared-on-typing behavior,
and you want to get the cleared-on-focus behavior, this is how to:
In case of proper Java code - it's a bit tricky since you can't define behavior of pseudo-classes directly.
Java Code (using bindings):
textField.styleProperty().bind(
Bindings
.when(textField.focusedProperty())
.then("-fx-prompt-text-fill: transparent;")
.otherwise("-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);"));
or -
Java Code (using events):
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
textField.setStyle("-fx-prompt-text-fill: transparent;");
} else {
textField.setStyle("-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);");
}
}
});
JavaFX FXML CSS:
Add the following to your CSS file.
.text-input {
-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);
}
.text-input:focused {
-fx-prompt-text-fill: transparent;
}
Hope this helped...
Solution
This example will allow TextFields in JavaFX whose prompt behaviour is to show the prompt when the field is empty, even if the field has focus. The solution is a combination of custom CSS and a custom TextField class, which manipulates the css styles of the TextField.
persistent-prompt.css
.persistent-prompt:focused {
-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);
}
.no-prompt {
-fx-prompt-text-fill: transparent !important;
}
PersistentPromptTextField.java
import javafx.scene.control.TextField;
public class PersistentPromptTextField extends TextField {
PersistentPromptTextField(String text, String prompt) {
super(text);
setPromptText(prompt);
getStyleClass().add("persistent-prompt");
refreshPromptVisibility();
textProperty().addListener(observable -> refreshPromptVisibility());
}
private void refreshPromptVisibility() {
final String text = getText();
if (isEmptyString(text)) {
getStyleClass().remove("no-prompt");
} else {
if (!getStyleClass().contains("no-prompt")) {
getStyleClass().add("no-prompt");
}
}
}
private boolean isEmptyString(String text) {
return text == null || text.isEmpty();
}
}
PromptChanger.java
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class PromptChanger extends Application {
#Override
public void start(Stage stage) throws Exception {
TextField textField1 = new PersistentPromptTextField("", "First name");
TextField textField2 = new PersistentPromptTextField("", "Last name");
VBox layout = new VBox(
10,
textField1,
textField2
);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
scene.getStylesheets().add(
getClass().getResource(
"persistent-prompt.css"
).toExternalForm()
);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
How prompt handling is currently implemented in JavaFX 8
Default CSS for JavaFX 8 (modena.css) for controlling the prompt text is as follows:
.text-input {
-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);
}
.text-input:focused {
-fx-prompt-text-fill: transparent;
}
This will make the prompt text transparent whenever a field is focused, even if the field has no data in it.
In Comparison to HTML
HTML input has a placeholder, which is specified as follows:
User agents should present this hint to the user . . . when the element's value is the empty string or the control is not focused (or both).
You can try this functionality in your browser at this test link.
I think the argument against this behaviour for JavaFX "Prompt text should be cleared when focus is grabbed to signal readiness to receiving user input" is moot because focused text fields get a clearly visible focus ring, so the user knows that the control is ready to receive input even when the prompt text is displayed.
I think JavaFX should by default operate the same way as most HTML user agents in this respect. Feel free to create a Tweak request in the JavaFX issue tracker to request that the input prompt in JavaFX work similarly to HTML implementations (if there isn't an issue created for this already).
Alternative
Glisten
The third-party Gluon Glisten has a custom TextField control can have the following attributes:
Float Text- A place-holder text inside a TextField which is transitioned to the top of the TextField when focus is received by it.
The nice thing about the Glisten-based TextField is that the prompt text is always visible, whether or not the user has typed anything into the control.
MaterialFX
MaterialFX also provides a variety of alternative implementations for text field prompts, similar to the Glisten operation, but matching the Google Material design. To see samples, click on the “Fields” demo option from the preview gifs.
You could configure the '-fx-prompt-text-fill' key in '.text-field:focused' area like in '.text-field' area (css):
`.text-field {
-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);
}
.text-field:focused {
-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);
}`
and add the 'promptText' entry in fxml file:
<TextField fx:id="XY_Id" promptText="First name">
That is all and works for me.
I am sure there may be several approaches and tricks to achieve what you're asking for, but, you are overriding a default, expected and standard UI behavior when you do so. Prompt text should be cleared when focus is grabbed to signal readiness to receiving user input, not after user starts typing.
Assuming you're setting prompt text via:
#FXML TextField tf;
public void someMethod() {
tf.setPromptText("This is the prompt text");
}
You can instead use tf.setText(String str) to set initial text.
Then, in the initialize method of your controller, add:
tf.setOnKeyTyped(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
tf.setText(null);
}
});
A link that you can refer to for additional help: Click me!
textField.setStyle("-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);");
or
textField.styleProperty().bind(
Bindings.createStringBinding(
()->"-fx-prompt-text-fill: derive(-fx-control-inner-background, -30%);"));

getRequestCycle().urlFor not working in panel

I need the URL for a component in wicket. When I use a page it works properly, but when using panel it does not work.
public final class ImageP extends Panel {
public ImageP(String id) {
super(id);
List<Mapp> list = Mapp.loadall(); //load image from database
final Mapp asr = list.get(0);
ByteArrayResource resource = new ByteArrayResource("image/jpeg", asr.getImage());
Image image = new Image("img", resource);
add(image);
System.out.println(getRequestCycle().urlFor(image, IResourceListener.INTERFACE));
}
}
This code does not work and throws an exception, but when I use page instead of panel getRequestCycle().urlFor(image, IResourceListener.INTERFACE) it works properly.
I bet you've got the following exception:
java.lang.IllegalStateException: No Page found for component [Component id = img]
It's because RequestCycle object internally calls getPage() method of the component that's first parameter of the urlFor() method with the following signature:
urlFor(Component component, RequestListenerInterface interface)
In case of calling method urlFor() in the constructor of a panel it's impossible to get page of a panel's child because panel isn't attached to page yet. So Wicket throws "a nice exception".
To fix that problem you just can move your code to the onBeforeRender() method of the panel. Something like that:
#Override
protected void onBeforeRender() {
//
// ... init resource ...
//
Image image = new Image("img", resource);
addOrReplace(image);
System.out.println(getRequestCycle().urlFor(image, IResourceListener.INTERFACE));
super.onBeforeRender();
}
P.S. I also assume that you're using Wicket 1.4 or earlier because there's no RequestCycle.urlFor(component, listener) method in Wicket 1.5 and later. So I think neither your question nor my answer doesn't make sense in that case.

Categories

Resources