I've created a vaadin table. When I do right-click it shows context menu with +New... text and when I click on it - it shows modal window with two tables. Every table has the same fuctionality.
The problem is that every time I open and close modal window it adds duplicates for context menu items on modal tables(on the main page it works correct). Moreover - it adds several modal windows when I click on modal table context menu (for example if I open window 5 times - it add 5 context menu items and 5 modal windows for clicked modal context menus)
The only way to return to one item - restart whole application.
What is the problem?
Every my table looks like this
#Component("taskTable")
#Scope("prototype")
public class TaskTable extends AbstractObjectTable {
#Autowired
private TaskService taskService;
#Autowired
private NewTaskWindow taskWindow;
#Autowired
private ShowTaskDetailsWindow detailsWindow;
private Action[] action = new Action[] { new Action("+New...") };
#Override
public Table createTable() {
caption = "Tasks";
headers = new String[] { "Description", "Project", "Status", "Weight", "Developer", "ID" };
this.addActionHandler(new Handler() {
#Override
public Action[] getActions(Object target, Object sender) {
return action;
}
#Override
public void handleAction(Action action, Object sender, Object target) {
switch(action.getCaption()) {
case "+New...": {
PmcUi.getCurrent().addWindow(taskWindow.createWindow());
break;
}
}
//what to do for action
}
});
this.addItemClickListener(new ItemClickListener(){
#Override
public void itemClick(ItemClickEvent event) {
if (event.isDoubleClick()) {
PmcUi.getCurrent().addWindow(detailsWindow.createWindow());
}
return;
}
});
return super.createTable();
}
#Override
protected IndexedContainer projectDatasource() {
IndexedContainer indexedContainer = new IndexedContainer();
for(String header: headers) {
indexedContainer.addContainerProperty(header, String.class, "");
}
List<Task> tasks = taskService.findAllTasks();
for(int i = 0; i < tasks.size(); i++) {
Object id = indexedContainer.addItem();
Task item = tasks.get(i);
indexedContainer.getContainerProperty(id, headers[0]).setValue(item.getDescription());
indexedContainer.getContainerProperty(id, headers[1]).setValue(item.getTaskProject());
indexedContainer.getContainerProperty(id, headers[2]).setValue(item.getStatus());
indexedContainer.getContainerProperty(id, headers[3]).setValue(item.getWeight());
indexedContainer.getContainerProperty(id, headers[4]).setValue(item.getTaskDeveloper());
indexedContainer.getContainerProperty(id, headers[5]).setValue(item.getTaskId());
}
return indexedContainer;
}
}
Where AbstractObjectTable
public abstract class AbstractObjectTable extends Table {
protected String caption;
protected String[] headers = null;
protected Table createTable() {
this.setContainerDataSource(projectDatasource());
this.setVisibleColumns(headers);
this.setSelectable(true);
this.setImmediate(true);
return this;
}
protected abstract IndexedContainer projectDatasource();
}
My +New... modal windows looks similar to that
#Component("newTaskWindow")
public class NewTaskWindow {
private Window createTaskWindow;
#Autowired
private TaskService taskService;
public Window createWindow() {
createTaskWindow = new Window("New Task");
initWindow();
fillWindow();
return createTaskWindow;
}
private void initWindow() {
createTaskWindow.setSizeUndefined();
createTaskWindow.setResizable(false);
createTaskWindow.setModal(true);
createTaskWindow.addCloseListener(new CloseListener() {
#Override
public void windowClose(CloseEvent e) {
Notification.show("Closed");
}
});
}
private void fillWindow() {
final TextField taskDescription = new TextField("Description");
final ComboBox taskProject = new ComboBox("Select project");
final ComboBox taskDeveloper = new ComboBox("Select developer");
final TextField taskWeight = new TextField("Task weight");
final TextField taskStatus = new TextField("Task status");
Button create = new Button("Create");
create.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Task task = new Task();
task.setTaskId(UUID.randomUUID().toString());
task.setStatus(taskStatus.getValue());
task.setTaskDeveloper(taskDeveloper.getValue().toString());
task.setTaskProject(taskProject.getValue().toString());
task.setWeight(taskWeight.getValue());
task.setDescription(taskDescription.getValue());
taskService.insertTask(task);
createTaskWindow.close();
}
});
Button close = new Button("Cancel");
close.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
createTaskWindow.close();
}
});
HorizontalLayout layout = new HorizontalLayout(create, close);
FormLayout formLayout = new FormLayout(taskProject, taskDeveloper, taskWeight, taskStatus,
taskDescription, layout);
formLayout.setMargin(true);
createTaskWindow.setContent(formLayout);
}
}
And my details windows also have similar architecture.
#Component("showTaskDetailsWindow")
public class ShowTaskDetailsWindow {
private Window showDetailsWindow;
#Autowired
private TaskService taskService;
public Window createWindow() {
showDetailsWindow = new Window("Show details");
initWindow();
fillWindow();
return showDetailsWindow;
}
private void initWindow() {
showDetailsWindow.setSizeUndefined();
showDetailsWindow.setResizable(false);
showDetailsWindow.setModal(true);
showDetailsWindow.addCloseListener(new CloseListener() {
#Override
public void windowClose(CloseEvent e) {
Notification.show("Closed");
}
});
}
private void fillWindow() {
final TextField taskDescription = new TextField("Description");
final TextField taskProject = new TextField("Task project");
final TextField taskDeveloper = new TextField("Task developer");
final TextField taskWeight = new TextField("Task weight");
final TextField taskStatus = new TextField("Task status");
FormLayout formLayout = new FormLayout(taskProject, taskDeveloper, taskWeight, taskStatus, taskDescription);
formLayout.setMargin(true);
showDetailsWindow.setContent(formLayout);
}
}
What is the problem? Why it is continuously multiplying?
The problem is your getActions implementation
#Override
public Action[] getActions(Object target, Object sender) {
return new Action[] { new Action("+New...")};
}
You should create one instance of the "new Action("+New...")" item and store it for example in the TaskTable object.
The getActions(..) should alsways return the same instance.
If you always create a new action, it just adds them to the already existing actions.
Looks like the createTable() method of the TaskTable class is called too many times but the provided code doesn't show where that method is called. That causes that multiple action handlers and item click listeners are added to a table.
Related
I have a boot app that I am adding some crud screens to. I decided to use Vaadin and it seems to work great until I deploy it to a multi-nodal production environment.
Once in the prod environment the screens constantly refresh for no apparent reason. For example there is a grid in one screen that when a row is clicked a dialog pops up that shows item details. but as soon as the dialog pops up the page refreshes numerous times.
This forum thread here https://vaadin.com/forum/thread/17586129/routerlayout-causing-page-refresh is an example of the same layout I am using and describes a very similar issue.
I have a base abstract class that extends VerticalLayout and all of the concrete classes extend this base abstract class. Each concrete class defines its own route and use a common layout class.
I have reached out on gitter, vaadin forum and opened a bug in github but no one from Vaadin want to respond to anything as far as I can tell.
Here are the versions of everything I am using:
Vaadin Flow version: 14
Java version: 12.0.1+12
OS version: Mac 10.14.5
Browser version: Fire Fox 70.0.1, Chrome 78.0.3904.97
Code snippets from my implementation:
Main View
#Slf4j
#RoutePrefix("v1/crud")
#Theme(value = Material.class, variant = Material.DARK)
public class MainView extends Div implements RouterLayout {
private H1 h1 = new H1("Vaadin Crud UI");
private HorizontalLayout header = new HorizontalLayout(h1);
private Div content = new Div();
private ApplicationContext context;
#Inject
public MainView(ApplicationContext context) {
this.context = context;
setSizeFull();
h1.setWidthFull();
content.setWidthFull();
header.setWidthFull();
header.setAlignItems(FlexComponent.Alignment.CENTER);
VerticalLayout navigationBar = new VerticalLayout();
navigationBar.setWidth("25%");
navigationBar.add(createNavigationButton("Home", VaadinIcon.HOME, ReportTab.class));
navigationBar.add(createNavigationButton("Batch Search", VaadinIcon.SEARCH, BatchSearchTab.class));
... a bunch more buttons
HorizontalLayout layout = new HorizontalLayout(navigationBar, content);
layout.setWidthFull();
VerticalLayout page = new VerticalLayout(header, layout);
page.setWidthFull();
add(page);
}
#Override
public void showRouterLayoutContent(HasElement hasElement) {
if (hasElement != null) {
Element newElement = hasElement.getElement();
if (newElement != null) {
content.removeAll();
content.getElement().appendChild(newElement);
}
}
}
private Button createNavigationButton(String caption, VaadinIcon icon, Class<? extends BaseEditor> editor) {
Button button = new Button(caption, icon.create());
button.addClickListener(event -> UI.getCurrent().navigate(editor));
button.addThemeVariants(ButtonVariant.MATERIAL_CONTAINED);
button.getStyle().set("background-color", "#00819D");
button.setWidthFull();
return button;
}
}
Base Component:
#Slf4j
#Data
#SpringComponent
#UIScope
public abstract class BaseEditor<P, B> extends VerticalLayout {
private final RememberMeService rememberMe;
private final P businessProcess;
protected Binder<B> binder;
protected Dialog editDialog = new Dialog();
protected Button save = new Button("Save", VaadinIcon.CHECK.create());
protected Button close = new Button("Close", VaadinIcon.EXIT.create());
protected Button delete = new Button("Delete", VaadinIcon.TRASH.create());
protected B bean;
private ChangeHandler changeHandler;
private boolean proceed = true;
public BaseEditor(P businessProcess, RememberMeService rememberMe) {
this.rememberMe = rememberMe;
this.businessProcess = businessProcess;
save.addClickListener(e -> save());
delete.addClickListener(e -> delete());
}
public abstract void delete();
public abstract void save();
protected abstract Component getContent();
protected void edit(B e) {
bean = e;
editDialog.open();
getBinder().setBean(e);
}
protected void initEditorPanel(Component... components) {
HorizontalLayout actions = new HorizontalLayout(save, close, delete);
VerticalLayout data = new VerticalLayout(components);
data.add(actions);
editDialog.removeAll();
editDialog.add(data);
getBinder().bindInstanceFields(this);
close.addClickListener(e -> editDialog.close());
}
public interface ChangeHandler {
void onChange();
}
void setChangeHandler(ChangeHandler h) {
changeHandler = h;
}
void errorDialog(String message) {
final Button close = new Button("Close", VaadinIcon.CLOSE.create());
H3 h3 = new H3(message);
final Dialog errorDialog = new Dialog(h3, close);
errorDialog.open();
close.addClickListener(e -> errorDialog.close());
}
BaseEditor filter(Predicate<B> predicate) {
Objects.requireNonNull(predicate);
proceed = predicate.test(bean);
return this;
}
void buttonConsumer(Consumer<B> consumer) {
if (!proceed) {
proceed = true;
return;
}
try {
consumer.accept(bean);
} catch (Exception e) {
errorDialog(e.getMessage());
} finally {
editDialog.close();
getChangeHandler().onChange();
}
}
void either(Consumer<B> whenTrue, Consumer<B> whenFalse) {
try {
if (proceed) {
whenTrue.accept(bean);
} else {
whenFalse.accept(bean);
}
} catch (Exception e) {
errorDialog(e.getMessage());
} finally {
proceed = true;
editDialog.close();
getChangeHandler().onChange();
}
}
}
Concrete Component:
#Slf4j
#Route(value = "search/batch", layout = MainView.class)
public class BatchSearchTab extends BaseEditor<BatchService, Batch> {
private TextField searchField1;
private TextField searchField2;
public BatchSearchTab(BatchService businessProcess, RememberMeService rememberMe) {
super(businessProcess, rememberMe);
binder = new Binder<>(Batch.class);
save.setIcon(VaadinIcon.REPLY.create());
save.setText("Replay");
delete.setIcon(VaadinIcon.CLOSE.create());
delete.setText("Cancel");
getContent();
}
#Override
public void delete() {
buttonConsumer(b -> getBusinessProcess().cancelBatch(b.getBatchId(), b.getUserAgent()));
}
#Override
public void save() {
filter(b -> b.isReplayable()).buttonConsumer(b -> getBusinessProcess().buildAndSendFile((getBean())));
}
#Override
public void edit(Batch batch) {
HorizontalLayout actions = new HorizontalLayout();
H2 h2 = new H2();
if (batch.isReplayable()) {
h2.setText("Would you like to replay the following.");
actions.add(save, delete, close);
} else {
h2.setText("This record is not eligible for replay.");
actions.add(close);
}
Label batchId = new Label("Correlation Id: " + batch.getBatchId());
Label txnCount = new Label("Transaction Count: " + batch.getTotalTxns());
Label txnAmount = new Label("Total: " + batch.getTotalBatchAmount());
VerticalLayout data = new VerticalLayout(h2, batchId, txnCount, txnAmount, actions);
data.add(actions);
editDialog.removeAll();
editDialog.add(data);
close.addClickListener(e -> editDialog.close());
editDialog.open();
getBinder().setBean(batch);
}
#Override
protected Component getContent() {
final H2 h2 = new H2("Locate Batches");
searchField1 = new TextField("Batch Code");
searchField2 = new TextField("User Agent");
searchField2.addKeyPressListener(Key.ENTER, e -> keyPressListener());
Button searchBtn = new Button("Search", VaadinIcon.SEARCH.create());
HorizontalLayout search = new HorizontalLayout(searchField1, searchField2);
searchBtn.addClickListener(e -> {
search(searchField1.getValue(), searchField2.getValue());
});
add(h2, search, searchBtn);
return this;
}
private void search(String code, String userAgent) {
log.info("Searching {} and {}", code, userAgent);
List<Batch> batches =
getBusinessProcess().getBatchesForUserAgent(code, userAgent, 60);
log.info("Found {} batches", batches.size());
if (batches.size() > 0) {
buildGrid(batches, "BatchId", "totalTxns", "totalBatchAmount", "status");
} else {
errorDialog("No Records found for criteria");
}
}
private void keyPressListener() {
String code = StringUtils.isNotBlank(searchField1.getValue()) ? searchField1.getValue() : null;
if (StringUtils.isNotBlank(searchField2.getValue())) {
search(code, searchField2.getValue());
}
}
private void buildGrid(Collection<Batch> records, String... columns) {
Component result;
if (records.size() == 0) {
result = new Label("NO REPORT DATA AVAILABLE.");
} else {
final Grid<Batch> grid = new Grid<>(Batch.class);
grid.setHeightByRows(records.size() < 10);
grid.setColumns(columns);
grid.setItems(records);
grid.setWidthFull();
grid.asSingleSelect().addValueChangeListener(l -> Optional.ofNullable(l.getValue()).ifPresent(this::edit));
result = grid;
}
if (getComponentCount() < 3) {
add(result);
} else {
replace(getComponentAt(2), result);
}
}
private void loadData(String code, String userAgent) {
if (StringUtils.isNotBlank(code)) {
search(null, userAgent);
} else {
search(code, userAgent);
}
}
}
Disclaimer: some further fact finding happended via IRC
The answer to the question is related to OP running multiple instances of the application behind an round robin load ballancer. The clients hit random servers and therefor had no session running there.
The solution to this is having a shared session store and ideally have the load ballancer dispatch on existing session, so "hot" backend servers get hit.
So I have been working on a project and I have a CharmListView that populates with the name of the task to be used. I can login through my login screen, get to the CharmListView and click on the task I want to have open. It opens on the Desktop when I am testing it, but in Android it fails, saying the location is not found, and that:
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.gluonhq.charm.glisten.mvc.View.setName(java.lang.String)' on a null object reference
Here are my charm classes that I have:
This one is the task model essentially.
public class CharmHomeNavTask {
private String taskName;
private String taskDesc;
private static final Image IMAGE_ADMIN = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/admin.png"));
private static final Image IMAGE_AUDIT_TOOL = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/auditTool.png"));
private static final Image IMAGE_CONSOLIDATE_PACKAGE = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/consolidatePackage.png"));
private static final Image IMAGE_DISPOSAL = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/disposal.png"));
private static final Image IMAGE_EQUIP_MANGAGE = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/equipmentManagement.png"));
private static final Image IMAGE_INTRA_TRANSFER = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/intratransfer.png"));
private static final Image IMAGE_PICKUP = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/pickup.png"));
private static final Image IMAGE_TRU_WASTE_PREP = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/truWastePrep.png"));
private static final Image IMAGE_VISUAL_INSPECTION = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/visualInspection.png"));
private static final Image IMAGE_WALL2WALL = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/wall2wall.png"));
private static final Image IMAGE_WASTE_ID = new Image(WcatsAndroidDemo.class.getResourceAsStream("/gov/lanl/taskImages/wasteIdentification.png"));
private static Image[] listOfImages = {IMAGE_WASTE_ID, IMAGE_VISUAL_INSPECTION, IMAGE_TRU_WASTE_PREP, IMAGE_CONSOLIDATE_PACKAGE, IMAGE_INTRA_TRANSFER, IMAGE_PICKUP, IMAGE_DISPOSAL, IMAGE_ADMIN, IMAGE_WALL2WALL,
IMAGE_EQUIP_MANGAGE, IMAGE_AUDIT_TOOL };
public CharmHomeNavTask(String taskName, String taskDesc){
this.taskName = taskName;
this.taskDesc = taskDesc;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public String getTaskDesc() {
return taskDesc;
}
public void setTaskDesc(String taskDesc) {
this.taskDesc = taskDesc;
}
public static void setListOfImages(Image[] listOfImages) {
listOfImages = listOfImages;
}
public static Image[] getListOfImages(){
return listOfImages;
}
public static Image getSingleImageFromList(int index){
return listOfImages[index];
}
}
This one is the creation of the tasks into an ObservableList
public class CharmHomeNavTasks {
public static ObservableList<CharmHomeNavTask> tasksList = FXCollections.observableArrayList(
new CharmHomeNavTask("Waste Identification", "Identify you waste from this screen."),
new CharmHomeNavTask("TRU Visual Inspection", "Visual Inspection of TRU waste."),
new CharmHomeNavTask("TRU Drum Preparation", "Prepare your TRU drums here."),
new CharmHomeNavTask("Consolidation/Packaging", "Consolidate and package your waste."),
new CharmHomeNavTask("Intra-Facility Transfer", "Transferring of waste within the same facility."),
new CharmHomeNavTask("Inter-Facility Pickup", "Picking up waste within the same facility."),
new CharmHomeNavTask("Disposal Tasks", "Disposal of waste tasks."),
new CharmHomeNavTask("Administrative Tasks", "Administrators have special tasks they can perform here."),
new CharmHomeNavTask("Wall-Wall Inventory", "Wall to Wall inventory tasks."),
new CharmHomeNavTask("Equipment Management", "Waste related equipment tasks."),
new CharmHomeNavTask("Audit Support Tool", "Tool for audit related tasks."));
public static ObservableList<CharmHomeNavTask> getTasksList() {
return tasksList;
}
}
This is the one that is giving me trouble because I have my onMouseClicked event here and I set the item(or task) to selected. This seems to work fine even on Android as I can the the system to tell me which item was clicked.
public class CharmHomeNavTaskCell extends CharmListCell<CharmHomeNavTask> {
private final ListTile tile;
private final ImageView imageView;
private CharmHomeNavTask item;
public CharmHomeNavTask carryOverItem;
public CharmHomeNavTaskCell(CharmListView listView){
tile = new ListTile();
imageView = new ImageView();
tile.setPrimaryGraphic(imageView);
carryOverItem = item;
tile.setOnMouseClicked(e -> {
System.out.println("******************* Item clicked " + item.getTaskName());
listView.setSelectedItem(item);
});
setText(null);
}
#Override
public void updateItem(CharmHomeNavTask item, boolean empty){
super.updateItem(item, empty);
this.item = item;
imageView.setFitWidth(32);
imageView.setFitHeight(32);
if (item != null && !empty) {
tile.textProperty().setAll(item.getTaskName() + " ", item.getTaskDesc());
tile.setWrapText(true);
final Image[] image = CharmHomeNavTask.getListOfImages();
super.setStyle("-fx-font-weight: bold");
switch (item.getTaskName()) {
case "Waste Identification":
imageView.setImage(image[0]);
break;
case "TRU Visual Inspection":
imageView.setImage(image[1]);
break;
case "TRU Drum Preparation":
imageView.setImage(image[2]);
break;
case "Consolidation/Packaging":
imageView.setImage(image[3]);
break;
case "Intra-Facility Transfer":
imageView.setImage(image[4]);
break;
case "Inter-Facility Pickup":
imageView.setImage(image[5]);
break;
case "Disposal Tasks":
imageView.setImage(image[6]);
break;
case "Administrative Tasks":
imageView.setImage(image[7]);
break;
case "Wall-Wall Inventory":
imageView.setImage(image[8]);
break;
case "Equipment Management":
imageView.setImage(image[9]);
break;
case "Audit Support Tool":
imageView.setImage(image[10]);
break;
}
setGraphic(tile);
} else {
setGraphic(null);
}
}
}
Here is the AppViewManager Class. I am using Glisten and Afterburner just so that you know.
public class AppViewManager {
private static String getLoggedInUser(User user){
if (user != null) {
return user.getId();
} else {
return "Not Logged In";
}
}
public static final AppViewRegistry REGISTRY = new AppViewRegistry();
public static final AppView PRIMARY_VIEW = view("Home", PrimaryPresenter.class, MaterialDesignIcon.HOME, SHOW_IN_DRAWER, HOME_VIEW);
public static final AppView SECONDARY_VIEW = view("Task List", SecondaryPresenter.class, MaterialDesignIcon.LIST, SHOW_IN_DRAWER);
public static final AppView SETTINGS_VIEW = view("Settings", SettingsView.class, MaterialDesignIcon.SETTINGS_APPLICATIONS, SHOW_IN_DRAWER);
public static final AppView INTRAFACILITYTRANSFER_VIEW = view("Intra-Facility Transfer", IntraFacilityView.class, MaterialDesignIcon.EDIT_LOCATION);
private static AppView view(String title, Class<? extends GluonPresenter<?>> presenterClass, MaterialDesignIcon menuIcon, AppView.Flag... flags ) {
return REGISTRY.createView(name(presenterClass), title, presenterClass, menuIcon, flags);
}
private static String name(Class<? extends GluonPresenter<?>> presenterClass) {
return presenterClass.getSimpleName().toUpperCase(Locale.ROOT).replace("PRESENTER", "");
}
public static void registerViewsAndDrawer(MobileApplication app) {
for (AppView view : REGISTRY.getViews()) {
view.registerView(app);
}
Image image = new Image(WcatsAndroidDemo.class.getResourceAsStream("/icon.png"));
NavigationDrawer.Header header = new NavigationDrawer.Header("\nWCATS" + "- " + getLoggedInUser(WcatsAndroidDemo.getInstance().getLoggedUser()),
"Waste Management System", new ImageView(image));
// TODO: Add a footer to the drawer that contains settings, help & feedback, and About
NavigationDrawer.Footer footer = new NavigationDrawer.Footer("No tasks currently need to be synchronized.", null);
//Create the sub items for the drawer
NavigationDrawer.Item about = new NavigationDrawer.Item("About", MaterialDesignIcon.INFO.graphic());
NavigationDrawer.Item logOut = new NavigationDrawer.Item("Logout", MaterialDesignIcon.EXIT_TO_APP.graphic());
// TODO: make the rest of the submenu items that go in the header.
DefaultDrawerManager drawerManager = new DefaultDrawerManager(app, header, REGISTRY.getViews()) {
{
NavigationDrawer drawer = getDrawer();
drawer.visibleProperty().addListener((observable, oldValue, newValue) -> {
if (newValue){
header.setTitle("\nWCATS - " + getLoggedInUser(WcatsAndroidDemo.getInstance().getLoggedUser()));
}
});
// Add items
drawer.setFooter(footer);
footer.setStyle("-fx-text-size: 6");
drawer.getItems().addAll(new Separator(), about, logOut, new Separator());
// TODO: provide action based on item selected
drawer.selectedItemProperty().addListener(((observable, oldValue, newValue) -> {
if(newValue.equals(about)) {
System.out.println("test");
} else if (newValue.equals(logOut)){
WcatsAndroidDemo.getInstance().userLogout();
} else if (newValue.equals(SECONDARY_VIEW.getMenuItem())){
if (getLoggedInUser(WcatsAndroidDemo.getInstance().getLoggedUser()).equals("Not Logged In")) {
AppViewManager.PRIMARY_VIEW.switchView();
} else {
AppViewManager.SECONDARY_VIEW.switchView();
}
}
} ));
}
};
drawerManager.installDrawer();
}
}
Lastly, here is the Presenter class for the view that I am navigation from and want to go to the task that is selected.
public class SecondaryPresenter extends GluonPresenter<WcatsAndroidDemo> {
#FXML
private View homeView;
#FXML
public CharmListView<CharmHomeNavTask, Integer> charmListView;
public void initialize() {
homeView.setShowTransitionFactory(BounceInRightTransition::new);
AppBar appBar = getApp().getAppBar();
homeView.showingProperty().addListener((observable, oldValue, newValue) -> {
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> getApp().showLayer(DRAWER_LAYER)));
appBar.setTitleText("Task Selection");
});
charmListView.setFloatingHeaderVisible(false);
charmListView.setItems(CharmHomeNavTasks.getTasksList());
charmListView.setCellFactory(param -> new CharmHomeNavTaskCell(charmListView));
charmListView.selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (newValue.getTaskName().equals("Intra-Facility Transfer")){
AppViewManager.INTRAFACILITYTRANSFER_VIEW.switchView(ViewStackPolicy.SKIP);
}
});
}
}
Just in case you do need it to figure anything out here is the view that I am trying to navigate to.
public class IntraFacilityView extends GluonPresenter<WcatsAndroidDemo> {
#FXML
public ChoiceBox facilityCMBox;
#FXML
public ChoiceBox storageUnitOrgCMBox;
#FXML
public ChoiceBox storageUnitDestCMBox;
#FXML
public ChoiceBox gridXCMBox;
#FXML
public ChoiceBox gridYCMBox;
#FXML
public ChoiceBox gridZCMBox;
#FXML
public CheckBox organizeUnitCHKBox;
#FXML
public Button viewMoreReqsBTN;
#FXML
public Button viewPendingMovesBTN;
#FXML
public Button resumeTaskBTN;
#FXML
private View intrafacility;
public void initialize(){
intrafacility.setShowTransitionFactory(BounceInRightTransition::new);
intrafacility.showingProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
AppBar appBar = getApp().getAppBar();
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> {
getApp().showLayer(DRAWER_LAYER);
}));
appBar.setTitleText("Intra-Facility Transfer");
appBar.getActionItems().add(MaterialDesignIcon.CLOSE.button(e -> {
AppViewManager.SECONDARY_VIEW.switchView();
}));
}
});
}
public void handleOrganizeUnitsCHKBox(ActionEvent actionEvent) {
}
public void handleViewMoreReqs(ActionEvent actionEvent) {
}
public void handleResumeTask(ActionEvent actionEvent) {
}
public void handlevVewPendingMoves(ActionEvent actionEvent) {
}
}
I know that some Android devices are not the best with JavaFxPorts, but I haven't seen anything about Panasonic toughpads having known issues like Samsung. I did have to do some janky stuff to get the abdroid devices to register the touches as clicks, but that seems to be working fine now.
This has me as a standstill as I can not figure out why Android can not find the location but it works fine on the desktop.
Here is the file structure:
Project structure
I am trying to find the best way to hide a UI component in a specific sub View but let it be visible in others.
This is the code of my UI class:
public class DCSAdminUI extends UI {
private static final long serialVersionUID = 1L;
VerticalLayout root = new VerticalLayout();
CssLayout content = new CssLayout();
private MenuBar nav = new MenuBar();
private MenuBar userNav = new MenuBar();
private Navigator navigator;
public static final String PERSISTENCE_UNIT = "mystery";
public static DCSAdminUI getCurrent() {
return (DCSAdminUI) UI.getCurrent();
}
public static DCSAdminUI getApplication() {
return (DCSAdminUI) getApplication();
}
#Override
protected void init(VaadinRequest request) {
Page.getCurrent().setTitle("Admin");
initLayout();
initNav();
initUserNav();
getUserOrg();
navigator = new Navigator(this, content);
LoginUI loginView = new LoginUI();
navigator.addView("", loginView);
navigator.addView(ActivitiesUI.VIEW_NAME.toLowerCase(), new ActivitiesUI());
navigator.addView(BookingsUI.VIEW_NAME.toLowerCase(), new BookingsUI());
navigator.addView(OperatorsUI.VIEW_NAME.toLowerCase(), new OperatorsUI());
navigator.addView(TeamUI.VIEW_NAME.toLowerCase(), new TeamUI());
navigator.addView(OrganisationsUI.VIEW_NAME.toLowerCase(), new OrganisationsUI());
navigator.addView(PlayersUI.VIEW_NAME.toLowerCase(), new PlayersUI());
navigator.addViewChangeListener(new ViewChangeListener() {
private static final long serialVersionUID = 1L;
#Override
public boolean beforeViewChange(ViewChangeEvent event) {
return true;
}
#Override
public void afterViewChange(ViewChangeEvent event) {
if (event.getViewName() == null || event.getViewName().equals("")) {
updateNavSelection("activities");
} else {
updateNavSelection(event.getViewName());
}
}
});
}
private void initLayout() {
root.setSizeFull();
setContent(root);
HorizontalLayout topbar = new HorizontalLayout();
topbar.addStyleName("topbar");
topbar.setWidth("100%");
topbar.setSpacing(true);
root.addComponent(topbar);
content.setSizeFull();
root.addComponent(content);
root.setExpandRatio(content, 1);
nav.addStyleName("nav");
topbar.addComponent(nav);
userNav.addStyleName("user-nav");
topbar.addComponent(userNav);
topbar.setComponentAlignment(userNav, Alignment.TOP_CENTER);
}
private void initNav() {
nav.addItem(ActivitiesUI.VIEW_NAME.toUpperCase(Locale.GERMANY), navCommand).setCheckable(true);
nav.addItem(BookingsUI.VIEW_NAME.toUpperCase(Locale.GERMANY), navCommand).setCheckable(true);
nav.addItem(PlayersUI.VIEW_NAME.toUpperCase(Locale.GERMANY), navCommand).setCheckable(true);
nav.addItem(TeamUI.VIEW_NAME.toUpperCase(Locale.GERMANY), navCommand).setCheckable(true);
nav.addItem(OperatorsUI.VIEW_NAME.toUpperCase(Locale.GERMANY), navCommand).setCheckable(true);
nav.addItem(OrganisationsUI.VIEW_NAME.toUpperCase(Locale.GERMANY), navCommand).setCheckable(true);
}
private void initUserNav() {
MenuItem username = userNav.addItem("DCS User", FontAwesome.CHEVRON_DOWN, null);
username.addItem("Edit Profile", null);
username.addItem("Settings", null);
username.addSeparator();
username.addItem("Logout", null);
}
private Command navCommand = new Command() {
private static final long serialVersionUID = 1L;
#Override
public void menuSelected(MenuItem selectedItem) {
updateNavSelection(selectedItem.getText());
navigator.navigateTo(selectedItem.getText().toLowerCase());
}
};
private void updateNavSelection(String selectedItem) {
for (MenuItem item : nav.getItems()) {
item.setChecked(item.getText().toLowerCase()
.equals(selectedItem.toLowerCase()));
}
}
void navigateTo(String menuItem) {
for (MenuItem item : nav.getItems()) {
if (item.getText().toLowerCase().equals(menuItem.toLowerCase())) {
navCommand.menuSelected(item);
return;
}
}
}
}
The UI component in question here is topbar in the initLayout() method which is the app's main MenuBar() but I want this to appear only for authenticated users, meaning that it should be hidden in LoginUI() (the default View class for now). My search efforts have led me to understand that I can use multiple UI classes but that this would eventually become tedious to maintain and that is why I would ideally like to find a way of hiding the topbar component in specific View classes
You can implement a ViewChangeListener in your UI and deal with your navbar in the beforeViewChange. Since this can also reject entering a View you can send anon users to the login too.
I would like to modify the right click context menu for some some SWT Text boxes.
I would like to still have some of the default options like Copy, Cut, Paste, but would also like to have a custom action 'Generate Random' to fill the text box with a UUID.
How can I add such a menu to the control?
here's what I came up with to add some of the standard functions (cut, copy, paste, select all) as well as a custom action (generate UUID)
public static void addContextMenuWithUUID(final Text control)
{
Menu menu = new Menu(control);
MenuItem item = new MenuItem(menu, SWT.PUSH);
item.setText("Cut");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.cut();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Copy");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.copy();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Paste");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.paste();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Select All");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.selectAll();
}
});
item = new MenuItem(menu, SWT.PUSH);
item.setText("Generate UUID");
item.addListener(SWT.Selection, new Listener()
{
#Override
public void handleEvent(Event event)
{
control.setText(UUID.randomUUID().toString());
}
});
control.setMenu(menu);
}
When I had to do something similar a while ago, this is what I did,
I adopted the TextActionHandler class provided by eclipse and modified the code to suit my needs.
public final class TextActionHandler {
enum TextAction {
CUT (WorkbenchMessages.Workbench_cut, IWorkbenchCommandConstants.EDIT_CUT),
COPY (WorkbenchMessages.Workbench_copy, IWorkbenchCommandConstants.EDIT_COPY),
PASTE (WorkbenchMessages.Workbench_paste, IWorkbenchCommandConstants.EDIT_PASTE),
DELETE (WorkbenchMessages.Workbench_delete, null),
SELECT_ALL(WorkbenchMessages.Workbench_selectAll, WorkbenchCommandConstants.EDIT_SELECT_ALL);
private String text;
private String commandId;
private TextAction(String text, String commandId ) {
this.text = text;
this.commandId = commandId;
}
public String getCommandId() {
return commandId;
}
public String getText() {
return text;
}
}
public TextActionHandler(Text text) {
addText(text);
}
public TextActionHandler() {
super();
}
public void addText(Text textControl) {
if (textControl == null) {
return;
}
textControl.addDisposeListener(new DisposeListener() {
#Override
public void widgetDisposed(DisposeEvent e) {
removeText(activeTextControl);
}
});
textControl.addListener(SWT.Activate, textControlListener);
textControl.addListener(SWT.Deactivate, textControlListener);
textControl.addKeyListener(keyAdapter);
textControl.addMouseListener(mouseAdapter);
activeTextControl = textControl;
updateActionsEnableState();
}
public void hookContextMenu() {
final MenuManager menuMgr = new MenuManager("#PMPopupMenu");
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
addContextMenuOptions(menuMgr);
}
});
Menu menu = menuMgr.createContextMenu(activeTextControl);
activeTextControl.setMenu(menu);
}
private void addContextMenuOptions(MenuManager manager) {
manager.removeAll();
manager.add(textCutAction);
manager.add(textCopyAction);
manager.add(textPasteAction);
manager.add(textDeleteAction);
manager.add(new Separator());
manager.add(textSelectAllAction);
// add your own action handlers here
}
...
// example.
private final class CutActionHandler extends Action {
private CutActionHandler() {
setProperties(this, TextAction.CUT);
setEnabled(false);
}
#Override
public void runWithEvent(Event event) {
if (activeTextControl != null && !activeTextControl.isDisposed()) {
activeTextControl.cut();
updateActionsEnableState();
}
}
#Override
public boolean isEnabled() {
return activeTextControl != null && !activeTextControl.isDisposed()
&& activeTextControl.getEditable()
&& activeTextControl.getSelectionCount() > 0;
}
public void updateEnabledState() {
setEnabled(isEnabled());
}
}
private void setProperties(Action action, TextAction actionEnum){
action.setText(actionEnum.getText());
action.setActionDefinitionId(actionEnum.getCommandId());
action.setImageDescriptor(getImageDescriptor(actionEnum));
action.setDisabledImageDescriptor(getDisabledImageDescriptor(actionEnum));
}
}
Likewise, you can have your own ActionHandlers added. e.g, RandomGeneratorHandler.
To hook this to your textboxes, do
Text text = new Text(parent, SWT.NONE);
...
TextActionHandler handler = new TextActionHandler();
handler.addText(text);
handler.hookContextMenu();
Note - I have not provided the complete class here, for other actions like copy, paste, delete and select all etc, you will have to do something similar as Cut. I have used the same code defined in the TextActionHandler class.
I've got window class implementation with annotation #Component. Inside this class I declare object with annotation #Autowired.
On my window form I've got a button Create which should read data from TextFields, create new object and store it in the database.
#Component("newProjectWindow")
public class NewProjectWindow {
private Window createProjectWindow;
#Autowired
private ProjectService service;
public Window createWindow() {
createProjectWindow = new Window("New project");
initWindow();
fillWindow();
return createProjectWindow;
}
private void initWindow() {
createProjectWindow.setSizeUndefined();
createProjectWindow.setResizable(false);
createProjectWindow.setModal(true);
createProjectWindow.addCloseListener(new CloseListener(){
#Override
public void windowClose(CloseEvent e) {
Notification.show("Closed");
}
});
}
private void fillWindow() {
final TextField projectName = new TextField("Project name");
final TextField projectOwner = new TextField("Project owner");
Button create = new Button("Create");
create.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Project newProject = new Project();
newProject.setProjectName(projectName.getValue());
newProject.setProjectOwner(projectOwner.getValue());
//save it somehow
}
});
Button close = new Button("Cancel");
close.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
createProjectWindow.close();
}
});
HorizontalLayout layout = new HorizontalLayout(create, close);
FormLayout formLayout = new FormLayout(projectName, projectOwner, layout);
formLayout.setMargin(true);
createProjectWindow.setContent(formLayout);
}
}
However the problem is how to store object in the database. I've got no access to instantiated ProjectService(which uses ProjectRepisitory which uses SqlSessionTemplate and etc.) because it is under control of Spring - and anonymous ClickListener is not.
But how to store object?
I tend not to use anonymous inner methods for click listeners, but instead get my own classes to implement the ClickListner. So in your example I would change the class like this:
#Component("newProjectWindow")
public class NewProjectWindow {
private Window createProjectWindow implements Button.ClickListener;
#Autowired
private ProjectService service;
private Button create = new Button("Create", this);
private Button cancel new Button("Cancel", this);;
public Window createWindow() {
createProjectWindow = new Window("New project");
initWindow();
fillWindow();
return createProjectWindow;
}
private void initWindow() {
createProjectWindow.setSizeUndefined();
createProjectWindow.setResizable(false);
createProjectWindow.setModal(true);
createProjectWindow.addCloseListener(new CloseListener(){
#Override
public void windowClose(CloseEvent e) {
Notification.show("Closed");
}
});
}
private void fillWindow() {
final TextField projectName = new TextField("Project name");
final TextField projectOwner = new TextField("Project owner");
HorizontalLayout layout = new HorizontalLayout(create, close);
FormLayout formLayout = new FormLayout(projectName, projectOwner, layout);
formLayout.setMargin(true);
createProjectWindow.setContent(formLayout);
}
#Override
public void buttonClick(ClickEvent event) {
if (event.getButton() == cancel)
{
createProjectWindow.close();
}
else
{
Project newProject = new Project();
newProject.setProjectName(projectName.getValue());
newProject.setProjectOwner(projectOwner.getValue());
//save it somehow
}
}
}
To access service from listener in your example, consider following solutions:
Anonymous inner classes can reference outer class (using OuterClassName.this syntax - in your case NewProjectWindow.this.service).
You can declare (inner) class and pass appropriate references to it.
You can use Chris M suggestion of parent class implementing listener interface itself.