JavaFX color as enum - javafx

I have a combobox that's initialized with public enum nameColor. I also have a shape that will pull the Color from that combobox. I can't seem to get my enum to represent the Color class. When I use foo.nameColor.BLUE as an argument for my shape, it doesn't recognize the value as a Color
public static enum nameColor {
AQUA(Color.AQUA),
BLACK(Color.BLACK),
BLUE(Color.BLUE),
CORAL(Color.CORAL),
GREEN(Color.GREEN),
GREY(Color.GREY),
RED(Color.RED),
WHITE(Color.WHITE),
YELLOW(Color.YELLOW);
private Color nameColor;
nameColor(Color nameColor){this.nameColor = nameColor;}
public void setNameColor(Color nameColor){this.nameColor = nameColor;}
public Color getNameColor(){return nameColor;}
}

Related

How to create validated TextField entensions in JavaFX

I want to create a JavaFX fxml based dialog, where the user can enter a bunch of integer and double values. I created the dialog in SceneBuilder using for each of the values a dedicated TextField.
Intentionally I am not using Binding between the TextFields and the model. In order to NOT add a ChangeListener or set a TextFormatter to each of these TextFields in the controller again and again, I created a dedicated IntegerTextField and DoubleTextField class, e.g.
public class IntegerTextField extends TextField {
protected static Pattern decimalPattern = Pattern.compile("^-?\\d+$"); // Double ("-?\\d*(\\.\\d{0,1})?");
public IntegerTextField() {
super();
setTextFormatter(new TextFormatter<>(c -> (decimalPattern.matcher(c.getControlNewText()).matches()) ? c : null ));
}
public int getInt() {
try {
return Integer.parseInt(getText());
}
catch (NumberFormatException e) {
return 0;
}
}
}
and in the Controller class I replaced the previous
#FXML private TextField setsTextField;
with
#FXML private IntegerTextField setsTextField;
When I got the
javafx.fxml.LoadException:...Can not set util.IntegerTextField field ctrl.ExerciseEditorCtrl.setsTextField to javafx.scene.control.TextField
I realized that this implicit downcasting doesn't work.
Is there a way to do this properly with fxml or is it neccessary to have the dialog setup in a java class when using IntegerTextField?

Why does setting BindableProperty.DefaultValue not call OnPropertyChanged?

I'm using a bindable property like this in a class the inherits from Xamarin.Forms.ContentView:
public static readonly BindableProperty OverlayColorProperty = BindableProperty.Create(nameof(OverlayColor), typeof(Color), typeof(MyControl), Color.FromHex("#55000000"));
public Color OverlayColor
{
get => (Color)GetValue(OverlayColorProperty);
set => SetValue(OverlayColorProperty, value);
}
Furthermore I'm listening for changes to update an inner elements background color:
protected override void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(propertyName);
switch (propertyName)
{
case nameof(OverlayColor):
GridBackground.BackgroundColor = OverlayColor;
break;
}
}
I just noticed that OnPropertyChanged does not get called with the default value. Just when I update it from another place, XAML or through code.
Is this exspected behavior?
If yes, why? What should I do instead? Define it also in XAML code?

ContentView -> Custom Frame Binding Not Working

I copied/wrote a class that inherits from Frame
public class Circle : Frame
{
//private double _radius;
public static readonly BindableProperty RadiusProperty = BindableProperty.Create(nameof(Radius), typeof(double), typeof(Circle), 126.0, BindingMode.TwoWay);
public double Radius
{
get => (double)GetValue(RadiusProperty); //_radius;
set
{
SetValue(RadiusProperty, value);
OnPropertyChanged();
AdjustSize();
}
}
private void AdjustSize()
{
HeightRequest = Radius;
WidthRequest = Radius;
Margin = new Thickness(0,0,0,0);
Padding = new Thickness(0, 0, 0, 0);
CornerRadius = (float) (Radius / 2);
}
public Circle()
{
HorizontalOptions = LayoutOptions.Center;
}
}
The consuming page defines these BinadableProperties
public static readonly BindableProperty InnerColorProperty = BindableProperty.Create("InnerColor", typeof(Color), typeof(CircleProgressView), defaultValue: Color.FromHex("#34495E"), BindingMode.TwoWay);
public Color InnerColor
{
get => (Color)GetValue(InnerColorProperty);
set => SetValue(InnerColorProperty, value);
}
public static readonly BindableProperty InnerRadiusProperty = BindableProperty.Create("InnerRadius", typeof(double), typeof(CircleProgressView), 126.0, BindingMode.TwoWay);
public double InnerRadius
{
get => (double)GetValue(InnerRadiusProperty);
set => SetValue(InnerRadiusProperty, value);
}
And uses the Circle like so
<components:Circle Grid.Row="0" BackgroundColor="{Binding InnerColor}" Radius="{Binding InnerRadius}" >
Alas, the bindable's setter, and hence AdjustSize(), is never called nor is the default value used. Instead of a circle I end up with a rectangle. The BackgroundColor, which is a property of Frame, binds and works fine.
If I remove the BindableProperty and leave behind a regular INotify property
public class Circle : Frame
{
private double _radius;
public double Radius
{
get => _radius;
set
{
_radius = value;
OnPropertyChanged();
AdjustSize();
}
}
private void AdjustSize()
{
HeightRequest = Radius;
WidthRequest = Radius;
Margin = new Thickness(0,0,0,0);
Padding = new Thickness(0, 0, 0, 0);
CornerRadius = (float) (Radius / 2);
}
public Circle()
{
HorizontalOptions = LayoutOptions.Center;
}
}
The compiler complains if I keep the InnerRadius binding
Severity Code Description Project File Line Suppression State
Error Position 17:92. No property, bindable property, or event found for 'Radius', or mismatching type between value and property. ...\Components\CircleProgressView.xaml 17
I can replace the Radius binding with a hardcoded value and it runs fine, a circle appears.
<components:Circle Grid.Row="0" BackgroundColor="{Binding InnerColor}" Radius="126" >
What's wrong with a BindableProperty in a regular C# class?
Firstly, we need to handle data in the property changed event of bindable property instead of the setter method of a normal property. So modify your Circle class like:
public static readonly BindableProperty RadiusProperty = BindableProperty.Create(nameof(Radius), typeof(double), typeof(Circle), 125.0, BindingMode.TwoWay, propertyChanged: RadiusChanged);
public double Radius
{
get => (double)GetValue(RadiusProperty); //_radius;
set => SetValue(RadiusProperty, value);
}
static void RadiusChanged(BindableObject bindableObject, object oldValue, object newValue)
{
Circle circle = bindableObject as Circle;
circle.HeightRequest = (double)newValue;
circle.WidthRequest = (double)newValue;
circle.CornerRadius = (float)((double)newValue / 2);
}
This is because we bind data in XAML we should manipulate the bindable property's changed event directly.
Secondly, I saw you bound the property using the parent page's bindable property. Normally, we won't do that. We will consume a view model as the page's binding context and then bind the property to the binding context. However, if you do want to consume the parent page's bindable property as the Circle's binding context, try this way:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Sample.SecondPage"
xmlns:components="clr-namespace:Sample"
x:Name="Page">
<ContentPage.Content>
<StackLayout>
<components:Circle BackgroundColor="{Binding InnerColor, Source={x:Reference Page}}" Radius="{Binding InnerRadius, Source={x:Reference Page}}"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Name your parent page first and change the circle's source to that.
Here, I used a different default Radius value comparing to InnerRadius so the property changed event will be called at the initial time.

Map integer to string of custom class in combobox

I have a tabelview that displays a list of appointees. Each appointe has a group assigned to it, the id of that group is saved in the appointe class.
I want to display a combobox inside a tablecell that displays the selected group and all other groups that exist. I can set the items of the combobox in the cell factory but i cant set the selected value of the respective appointee.
I have a method that returns the Group from the observable list when i provide it with the id. Thats means i need the id in the cellfactory but i didnt find a way to do this. I also need to display the name of the group and not the refernce to the clas. Is there a way to do this, or should i change my approach?
The Appointee class
public class Appointee {
private SimpleIntegerProperty id;
private SimpleStringProperty firstname;
private SimpleStringProperty lastname;
private SimpleIntegerProperty group;
private SimpleIntegerProperty assigned;
public Appointee(int id, String firstname, String lastname, int group, int assigned){
this.id = new SimpleIntegerProperty(id);
this.firstname = new SimpleStringProperty(firstname);
this.lastname = new SimpleStringProperty(lastname);
this.group = new SimpleIntegerProperty(group);
this.assigned = new SimpleIntegerProperty(assigned);
}
The Group class
public class Group {
private IntegerProperty id;
private StringProperty name;
private IntegerProperty members;
private IntegerProperty assigned;
public Group(int id, String name, int members, int assigned) {
this.id = new SimpleIntegerProperty(id);
this.name = new SimpleStringProperty(name);
this.members = new SimpleIntegerProperty(members);
this.assigned = new SimpleIntegerProperty(assigned);
}
The appointe table view
public AppointeeTableView() {
// define table view
this.setPrefHeight(800);
this.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
this.setItems(MainController.appointeeObervableList);
this.setEditable(true);
// define columns
...
TableColumn groupCol = new TableColumn("Group"); // group
groupCol.setCellFactory(col -> {
TableCell<Group, StringProperty> c = new TableCell<>();
final ComboBox<String> comboBox = new ComboBox(MainController.groupObservableList);
c.graphicProperty().bind(Bindings.when(c.emptyProperty()).then((Node) null).otherwise(comboBox));
return c;
});
groupCol.setEditable(false);
...
}
Override the updateItem method of the TableCell to update the cell, make sure the new value is saved on a change of the TableCell value and use a cellValueFactory.
final Map<Integer, Group> groupById = ...
final ObservableList<Integer> groupIds = ...
TableColumn<Group, Number> groupCol = new TableColumn<>("Group");
groupCol.setCellValueFactory(cd -> cd.getValue().groupProperty());
class GroupCell extends ListCell<Integer> {
#Override
protected void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
Group group = groupById.get(item);
if (empty || group == null) {
setText("");
} else {
setText(group.getName());
}
}
}
groupCol.setCellFactory(col -> new TableCell<Group, Integer>() {
private final ComboBox<Integer> comboBox = new ComboBox<>(groupIds);
private final ChangeListener<Integer> listener = (o, oldValue, newValue) -> {
Group group = (Group) getTableView().getItems().get(getIndex());
group.setGroup(newValue);
};
{
comboBox.setCellFactory(lv -> new GroupCell());
comboBox.setButtonCell(new GroupCell());
}
#Override
protected void updateItem(Number item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
comboBox.valueProperty().removeListener(listener);
setGraphic(comboBox);
comboBox.setValue((Integer) item);
comboBox.valueProperty().addListener(listener);
}
}
});
It's a bit hard to tell from only some small code snippets, but my general recommendation when working with frontends is to distinguish between the model and the rendering on each level. This applies to JavaFX, Swing and Angular applications alike.
The appointee TableView should likely be TableView<Appointee>.
For the appointee.group property you have two options: either use Group or (e.g. when this would generate too many duplicate data when de-/serializing from/ to JSON) then use a business key. The first option is usually easier to implement and work with. With the second option you'll need some service / code to convert back to a Group and have to think about where/ at what level exactly you want to do the conversion.
Let's go on here with the second option as you currently have specified appointee.group to be an integer.
In this case the group column should be TableColum<Appointee, Integer>.
The group cell then should be TableCell<Appointee, Integer>.
So far we've only talked about the model, not about rendering except that we want to display the appointees in a table.
I recommend to do this also on the next level.
Don't use a ComboBox<String> for a groups comboBox but a ComboBox<Group>. String is how you want to render the group inside the comboBox but the Group is the model. Also ComboBox<Integer>, the type of the business key, is a bit misleading (as you want a Groups comboBox, not an integer comboBox) and limits the flexibility of your code.
Use the converting service / code I've mentioned when pre-selecting a value in the comboBox.
The group cell should have the type ListCell<Group> and in the updateItem method, which concerns about how to render a Group, you could e.g. use the name property to get the String representation.
Of course there are variations of this approach, but make sure that on each level you know what the model of the control is and what the renderer of the control is. Always design your code using the model and use the rendering types only at the lowest rendering level.

How can i replicate GMail current hovered row marker with GWT?

I just want to know how would you do, with a GWT CellTable, to replicate the GMail hovered row marker:
I created this classes:
public class MarkerCell extends ImageResourceCell {
#Override
public void render(com.google.gwt.cell.client.Cell.Context context, ImageResource value, SafeHtmlBuilder sb) {
value = Images.INSTANCE.selectedRowMarker();
super.render(context, value, sb);
}
}
public class MarkerColumn<T> extends Column<T, ImageResource> {
public MarkerColumn() {
super(new MarkerCell());
}
#Override
public ImageResource getValue(T object) {
return Images.INSTANCE.selectedRowMarker();
}
}
and i used it in this way:
MarkerColumn<GfsFile> marker = new MarkerColumn<GfsFile>();
marker.setSortable(false);
table.addColumn(marker);
Excluding the hover event handling that should hide/unhide the marker, the result is not quite as i expected:
What approach would you use to get closer to GMail?
I would embed the row marker image as part of the each row and attach a style that sets the opacity to 0. Then add a :hover state to the row selectorClass that sets the opacity to 1. The trick is getting the css selector right. I think cell table uses table rows so it should be something like
tr:hover .selectorImageStyle {opacity:1 }

Resources