Create JavaFX ComboBox with custom Popup - javafx

I want to write a ComboBox with a custom Node object inside its Popup (rather than the common ListView). ColorPicker and DatePicker are good examples, which are the other two implementations of ComboBoxBase. I had thought I could easily extend ComboBoxBase, too, but since there is no popupProperty or popupFactory I don't know how to set the content. How else is it meant to be done? Or how ColorPicker and DatePicker do this?

ComboBoxPopupControl which extends ComboBoxBaseSkin contains getPopupContent(). That's the method you are looking for. In your own skin implementation, which extends one of the ComboBoxSkins, you can return the popup content you like (although it's not recommended to use private API)
public class CustomComboBox<T> extends ComboBox<T> {
#Override
protected Skin<?> createDefaultSkin() {
return new CustomComboBoxSkin<>(this);
}
}
public class CustomComboBoxSkin<T> extends ComboBoxPopupControl<T> {
public CustomComboBoxSkin(ComboBox<T> comboBox) {
super(comboBox, new CustomComboBoxBehaviour<>(comboBox));
}
#Override
public Node getPopupContent() {
return new Rectangle(150, 200);
}
// inherited methods ...
}

I used ComtextMenu to replace the commbox's popup like this:
ContextMenu menu = new ContextMenu();
MenuItem item = new MenuItem();
item.setGraphic(new Lable("test"));
menu.getItems.add(item);
commbox.setContextMenu(null);
commbox.setContextMenu(menu );
commbox.getContextMenu().show(comboBox, Side.BOTTOM, 0, 0);
It works fine.

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?

JavaFX tableview auto scroll to selected item when pressing a button to selectNext() or selectPrevious()

I'm writing a JavaFX program with a TableView called 'table' and 2 buttons called 'previous' & 'next'.
Here is part of the code:
previous.setOnAction(event -> {
table.getSelectionModel().selectPrevious();
});
next.setOnAction(event -> {
table.getSelectionModel().selectNext();
});
However, if I keep pressing the buttons, the table will not scroll automatically to keep the selected item visible. So I modified the code like this :
previous.setOnAction(event -> {
table.getSelectionModel().selectPrevious();
table.scrollTo(table.getSelectionModel().getSelectedItem());
});
next.setOnAction(event -> {
table.getSelectionModel().selectNext();
table.scrollTo(table.getSelectionModel().getSelectedItem());
});
But it will always try to keep the selected item at the top of the visible region. If I keep pressing 'next'. The selected item will stay at the top instead of staying at the bottom.
I want to mimic the natural behavior of a tableview in the way that if I press up or down on the keyboard with something selected, the tableview will scroll automatically to keep the selected item visible.
How should I modify the code to make the auto scrolling more natural when I press the buttons?
Thanks
The problem is
missing fine-grained control of scrollTo target location on application level
the (somewhat unfortunate) implementation of virtualizedControl.scrollTo(index) which (ultimately) leads to calling flow.scrollToTop(index)
There's a long-standing RFE (reported 2014!) requesting better control from application code. Actually, VirtualFlow has public methods (scrollToTop, scrollTo, scrollPixels) providing such, only they are not passed on to the control layer (getVirtualFlow in VirtualContainerBase is final protected), so can't be overridden in a custom skin. Since fx12, we can hack a bit, and expose the onSelectXX of Tree/TableViewSkin and use those, either directly in application code (example below) or in a custom TableView.
Example code:
public class TableSelectNextKeepVisible extends Application {
/**
* Custom table skin to expose onSelectXX methods for application use.
*/
public static class MyTableSkin<T> extends TableViewSkin<T> {
public MyTableSkin(TableView<T> control) {
super(control);
}
/**
* Overridden to widen scope to public.
*/
#Override
public void onSelectBelowCell() {
super.onSelectBelowCell();
}
/**
* Overridden to widen scope to public.
*/
#Override
public void onSelectAboveCell() {
super.onSelectAboveCell();
}
}
private Parent createContent() {
TableView<Locale> table = new TableView<>(FXCollections.observableArrayList(Locale.getAvailableLocales())) {
#Override
protected Skin<?> createDefaultSkin() {
return new MyTableSkin<>(this);
}
};
TableColumn<Locale, String> country = new TableColumn<>("Column");
country.setCellValueFactory(new PropertyValueFactory<>("displayLanguage"));
table.getColumns().addAll(country);
Button next = new Button("next");
next.setOnAction(e -> {
table.getSelectionModel().selectNext();
// scrolls to top
// table.scrollTo(table.getSelectionModel().getSelectedIndex());
((MyTableSkin<?>) table.getSkin()).onSelectBelowCell();
});
Button previous = new Button("previous");
previous.setOnAction(e -> {
table.getSelectionModel().selectPrevious();
// scrolls to top
// table.scrollTo(table.getSelectionModel().getSelectedIndex());
((MyTableSkin<?>) table.getSkin()).onSelectAboveCell();
});
BorderPane content = new BorderPane(table);
content.setBottom(new HBox(10, next, previous));
return content;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
try using getSelectedIndex as follows instead of using getSelectedItem
previous.setOnAction(event -> {
table.getSelectionModel().selectPrevious();
table.scrollTo(table.getSelectionModel().getSelectedIndex());
});
Platform.runLater( () -> TABLE_NAME.scrollTo(TABLE_INFORMATION_LIST.getList().size()-index) );
should work if you call it whenever you add information to the table.

vaadin FormLayout set custom left:right component's

In a vaadin FormLayout i want to show multiple components(field).
You can build custom Component that extends com.vaadin.ui.CustomComponent (example below).
If you want to use fields binding, things get more complicated and you should probably extend com.vaadin.ui.AbstractField and override some more methods.
SuperCustom dateSelection = new SuperCustom("Caption on left side in FormLayout");
formLayout.addComponent(dateSelection);
class SuperCustom extends CustomComponent {
ComboBox ordinal = new ComboBox();
ComboBox day = new ComboBox();
ComboBox month = new ComboBox();
public SuperCustom(String caption) {
setCompositionRoot(new HorizontalLayout(ordinal, day, new Label("of"), month));
configureComponents();
setCaption(caption);
}
private void configureComponents() {
//fill comboboxes
}
public Date getValue() {
//build date based on fields
return date;
}
}

afterburner.fx get view from node

i've been trying to get the grasp of afterburner.fx for few days now but i cant figure out this problem. please help
there are three tabs in a tab pane
tabPane.getTabs().get(0).setContent(new FirstView().getView());
tabPane.getTabs().get(1).setContent(new SecondView().getView());
tabPane.getTabs().get(2).setContent(new ThirdView().getView());
these are not named firstview, secondview etc. it's for demonstration...
now each of these views have a reload method:
firstView.reload()
secondView.reload()
thirdView.reload()
and i have setup a listener for tab changes so that i can reload these views once they come into view
tabPane.getSelectionModel().selectedItemProperty().addListener((o, oldValue, newValue) -> {
newValue.getView().reload(); // of course this cant be done like this
})
how to reload the view of the tab once it comes into view.?
You can add listeners to the individual tabs instead of to the tab pane:
FirstView firstView = new FirstView();
Tab tab0 = tabs.getTabs().get(0);
tab0.selectedProperty().addListener((obs, wasSelected, isSelected) -> {
if (isSelected) {
firstView.reload();
}
});
tab0.setContent(firstView.getView());
// etc
i managed to find a work around for this. i made two new classes, one that extends FXMLView and another that extends Tab.
public abstract class ReloadableView extends FXMLView {
public abstract void reload();
}
and
public class ReloadingTab<V extends ReloadableView> extends Tab {
private V view;
public ReloadingTab(String text, V view) {
super(text, view.getView());
this.view = view;
}
public V getView() {
return view;
}
}
and finally added the listener
tabPane.getSelectionModel().selectedItemProperty().addListener((o, oldValue, newValue) -> {
ReloadingTab tab = (ReloadingTab) newValue;
tab.getView().reload();
});
sorry if my question didnt make any sense. i hope someone finds this useful.

Structuring a MonoTouch.Dialog application

From the examples at Xamarin.com you can build basic M.T. Dialog apps, but how do you build a real life application?
Do you:
1) Create a single DialogViewController and tree every view/RootElement from there or,
2) Create a DialogViewController for every view and use the UINavigationController and push it on as needed?
Depending on your answer, the better response is how? I've built the example task app, so I understand adding elements to a table, click it to go to the 'next' view for editing, but how to click for non-editing? How to click a button, go next view if answer is number 1?
Revised:
There is probably no one right answer, but what I've come up with seems to work for us. Number 2 from above is what was chosen, below is an example of the code as it currently exists. What we did was create a navigation controller in AppDelegate and give access to it throughout the whole application like this:
public partial class AppDelegate : UIApplicationDelegate
{
public UIWindow window { get; private set; }
//< There's a Window property/field which we chose not to bother with
public static AppDelegate Current { get; private set; }
public UINavigationController NavController { get; private set; }
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
Current = this;
window = new UIWindow (UIScreen.MainScreen.Bounds);
NavController = new UINavigationController();
// See About Controller below
DialogViewController about = new AboutController();
NavController.PushViewController(about, true);
window.RootViewController = NavController;
window.MakeKeyAndVisible ();
return true;
}
}
Then every Dialog has a structure like this:
public class AboutController : DialogViewController
{
public delegate void D(AboutController dvc);
public event D ViewLoaded = delegate { };
static About about;
public AboutController()
: base(about = new About())
{
Autorotate = true;
about.SetDialogViewController(this);
}
public override void LoadView()
{
base.LoadView();
ViewLoaded(this);
}
}
public class About : RootElement
{
static AboutModel about = AboutVM.About;
public About()
: base(about.Title)
{
string[] message = about.Text.Split(...);
Add(new Section(){
new AboutMessage(message[0]),
new About_Image(about),
new AboutMessage(message[1]),
});
}
internal void SetDialogViewController(AboutController dvc)
{
var next = new UIBarButtonItem(UIBarButtonSystemItem.Play);
dvc.NavigationItem.RightBarButtonItem = next;
dvc.ViewLoaded += new AboutController.D(dvc_ViewLoaded);
next.Clicked += new System.EventHandler(next_Clicked);
}
void next_Clicked(object sender, System.EventArgs e)
{
// Load next controller
AppDelegate.Current.NavController.PushViewController(new IssuesController(), true);
}
void dvc_ViewLoaded(AboutController dvc)
{
// Swipe location: https://gist.github.com/2884348
dvc.View.Swipe(UISwipeGestureRecognizerDirection.Left).Event +=
delegate { next_Clicked(null, null); };
}
}
Create a sub-class of elements as needed:
public class About_Image : Element, IElementSizing
{
static NSString skey = new NSString("About_Image");
AboutModel about;
UIImage image;
public About_Image(AboutModel about)
: base(string.Empty)
{
this.about = about;
FileInfo imageFile = App.LibraryFile(about.Image ?? "filler.png");
if (imageFile.Exists)
{
float size = 240;
image = UIImage.FromFile(imageFile.FullName);
var resizer = new ImageResizer(image);
resizer.Resize(size, size);
image = resizer.ModifiedImage;
}
}
public override UITableViewCell GetCell(UITableView tv)
{
var cell = tv.DequeueReusableCell(skey);
if (cell == null)
{
cell = new UITableViewCell(UITableViewCellStyle.Default, skey)
{
SelectionStyle = UITableViewCellSelectionStyle.None,
Accessory = UITableViewCellAccessory.None,
};
}
if (null != image)
{
cell.ImageView.ContentMode = UIViewContentMode.Center;
cell.ImageView.Image = image;
}
return cell;
}
public float GetHeight(UITableView tableView, NSIndexPath indexPath)
{
float height = 100;
if (null != image)
height = image.Size.Height;
return height;
}
public override void Selected(DialogViewController dvc, UITableView tableView, NSIndexPath indexPath)
{
//base.Selected(dvc, tableView, path);
tableView.DeselectRow(indexPath, true);
}
}
#miquel
The current idea of a workflow is an app that starts with a jpg of the Default.png that fades into the first view, with a flow control button(s) that would move to the main app. This view, which I had working previous to M.T.D. (MonoTouch.Dialog), which is a table of text rows with an image. When each row is clicked, it moves to another view that has the row/text in more detail.
The app also supports in-app-purchasing, so if the client wishes to purchase more of the product, then switch to another view to transact the purchase(s). This part was the main reason for switching to M.T.D., as I thought M.T.D. would be perfect for it.
Lastly there would be a settings view to re-enable purchases, etc.
PS How does one know when the app is un-minimized? We would like to show the fade in image again.
I have been asking myself the same questions. I've used the Funq Dependency Injection framework and I create a new DialogViewController for each view. It's effectively the same approach I've used previously developing ASP.NET MVC applications and means I can keep the controller logic nicely separated. I subclass DialogViewController for each view which allows me to pass in to the controller any application data required for that particular controller. I'm not sure if this is the recommended approach but so far it's working for me.
I too have looked at the TweetStation application and I find it a useful reference but the associated documentation specifically says that it isn't trying to be an example of how to structure a MonoTouch application.
I use option 2 that you stated as well, it works pretty nicely as you're able to edit the toolbar options on a per-root-view basis and such.
Option 2 is more feasible, as it also gives you more control on each DialogViewController. It can also helps if you want to conditionally load the view.

Resources