Skip to content

Commit

Permalink
#537 improve custom panel support embed control
Browse files Browse the repository at this point in the history
  • Loading branch information
davetcc committed Nov 9, 2024
1 parent 7140e47 commit 4678c15
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import static com.thecoderscorner.embedcontrol.customization.FontInformation.SizeMeasurement.PERCENT;
Expand All @@ -29,7 +27,6 @@ public abstract class MenuGridComponent<T> {
private final MenuTree tree;
protected final Map<Integer, EditorComponent<T>> editorComponents = new HashMap<>();
private final ScheduledExecutorService executor;
private final ScheduledFuture<?> remoteTickTask;
private ThreadMarshaller marshaller;
private int row;

Expand All @@ -40,12 +37,8 @@ public MenuGridComponent(MenuItemStore store, JfxNavigationManager navMgr, Sched
this.navMgr = navMgr;
this.executor = executor;
this.marshaller = marshaller;

remoteTickTask = executor.scheduleAtFixedRate(this::timerTick, 100, 100, TimeUnit.MILLISECONDS);
}



public void renderMenuRecursive(MenuEditorFactory<T> editorFactory, SubMenuItem sub, boolean recurse, int level) {
Consumer<MenuItem> subRenderer = subMenuItem -> navMgr.pushMenuNavigation(asSubMenu(subMenuItem), menuItemStore);
if (menuItemStore.hasSubConfiguration(sub.getId())) {
Expand Down Expand Up @@ -112,12 +105,6 @@ public void itemHasUpdated(MenuItem item) {
}
}

public void tickAll() {
for(var component : editorComponents.values()) {
component.tick();
}
}

private ComponentSettings getComponentForMenuItem(MenuItem item) {
var position = defaultSpaceForItem(Optional.of(item));

Expand Down Expand Up @@ -158,6 +145,12 @@ public void acknowledgementReceived(CorrelationId key, AckStatus status) {
}
}

public void tickAll() {
for(var comp : editorComponents.values()) {
comp.tick();
}
}

public record ScreenLayoutBasedConditionalColor(MenuItemStore store, ComponentPositioning where) implements ConditionalColoring {
@Override
public PortableColor foregroundFor(EditorComponent.RenderingStatus status, ColorComponentType compType) {
Expand Down Expand Up @@ -196,19 +189,6 @@ private ControlColor getControlColor(EditorComponent.RenderingStatus status, Col
}
}

/**
* Called frequently by the executor to update the UI components.
*/
public void timerTick() {
marshaller.runOnUiThread(this::tickAll);
}

/**
* Call this to stop the associated tasks.
*/
public void dispose() {
remoteTickTask.cancel(false);
}

public static ControlType defaultControlForType(MenuItem item) {
if (item instanceof SubMenuItem || item instanceof BooleanMenuItem || item instanceof ActionMenuItem) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import com.thecoderscorner.embedcontrol.core.service.GlobalSettings;
import com.thecoderscorner.embedcontrol.customization.FontInformation;
import com.thecoderscorner.embedcontrol.customization.MenuItemStore;
import com.thecoderscorner.menu.domain.MenuItem;
import com.thecoderscorner.menu.domain.SubMenuItem;
import com.thecoderscorner.menu.domain.state.MenuTree;
import com.thecoderscorner.menu.domain.util.MenuItemFormatter;
import javafx.application.Platform;
import com.thecoderscorner.menu.remote.commands.AckStatus;
import com.thecoderscorner.menu.remote.protocol.CorrelationId;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Node;
Expand All @@ -23,7 +25,7 @@
import static com.thecoderscorner.embedcontrol.core.controlmgr.color.ControlColor.asFxColor;
import static com.thecoderscorner.embedcontrol.customization.FontInformation.SizeMeasurement;

public class JfxMenuPresentable implements PanelPresentable<Node> {
public class JfxMenuPresentable implements PanelPresentable<Node>, UpdatablePanel {
private final SubMenuItem subMenuItem;
protected final MenuGridComponent<Node> gridComponent;
private final MenuEditorFactory<Node> editorFactory;
Expand Down Expand Up @@ -89,8 +91,24 @@ public boolean canClose() {
public void closePanel() {
}

@Override
public void connectionIsUp(boolean up) {
Platform.runLater(()->gridPane.setDisable(!up));
gridPane.setDisable(!up);
}

@Override
public void acknowledgedCorrelationId(CorrelationId correlationId, AckStatus status) {
getGridComponent().acknowledgementReceived(correlationId, status);
}

@Override
public void tickAll() {
getGridComponent().tickAll();
}

@Override
public void itemHasUpdated(MenuItem item) {
getGridComponent().itemHasUpdated(item);
}

class JfxGridComponent extends MenuGridComponent<Node> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;

import static javafx.scene.control.Alert.AlertType;
Expand Down Expand Up @@ -158,6 +159,13 @@ public void pushMenuNavigation(SubMenuItem subMenuItem, MenuItemStore store, boo
}
runNavigation(presentable);
navigationStack.push(presentable);
executorService.scheduleAtFixedRate(() -> {
Platform.runLater(() -> {
if(currentNavigationPanel() instanceof UpdatablePanel updatablePanel) {
updatablePanel.tickAll();
}
});
}, 100L, 100L, TimeUnit.MILLISECONDS);
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.thecoderscorner.embedcontrol.jfx.controlmgr;

import com.thecoderscorner.menu.domain.MenuItem;
import com.thecoderscorner.menu.remote.commands.AckStatus;
import com.thecoderscorner.menu.remote.protocol.CorrelationId;

/// Represents a `PanelPresentable` that can be updated when menu items change, it is also provided with a
/// tick function so that the implementor can tick down updates that occur. When an item updates the update
/// will be sent through the `itemHasUpdated` method, and you will be on the JavaFx thread when it occurs.
public interface UpdatablePanel {
/// called every 100 millis by the framework so that you can tick any animations that are in progress.
void tickAll();

/// called whenever there is a menu item update so that the display can be updated.
/// @param item the item that has updated
void itemHasUpdated(MenuItem item);

/// called whenever the connection state changes.
/// @param isUp true if connection up, otherwise false
void connectionIsUp(boolean isUp);

/// called whenever an acknowledgement correlation ID is received
/// @param correlationId the correlation id of the acknowledgement
/// @param status the status of the acknowledgement
void acknowledgedCorrelationId(CorrelationId correlationId, AckStatus status);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.thecoderscorner.embedcontrol.jfx.controlmgr.panels;

import com.thecoderscorner.embedcontrol.core.controlmgr.*;
import com.thecoderscorner.embedcontrol.core.controlmgr.color.ConditionalColoring;
import com.thecoderscorner.embedcontrol.jfx.controlmgr.UpdatablePanel;
import com.thecoderscorner.menu.domain.MenuItem;
import com.thecoderscorner.menu.domain.state.MenuTree;
import com.thecoderscorner.menu.remote.commands.AckStatus;
import com.thecoderscorner.menu.remote.protocol.CorrelationId;
import javafx.scene.Node;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.GridPane;

import java.util.HashMap;

import static com.thecoderscorner.embedcontrol.core.controlmgr.EditorComponent.RenderingStatus.NORMAL;
import static com.thecoderscorner.embedcontrol.core.controlmgr.color.ConditionalColoring.ColorComponentType.TEXT_FIELD;
import static com.thecoderscorner.embedcontrol.core.controlmgr.color.ControlColor.asFxColor;

public abstract class BaseCustomMenuPanel implements PanelPresentable<Node>, UpdatablePanel {
protected final boolean panelCanBeClosed;
protected final MenuEditorFactory<Node> editorFactory;
protected final ConditionalColoring conditionalColoring;
protected final MenuTree menuTree;
protected final HashMap<Integer, EditorComponent<Node>> controlsBeingManaged = new HashMap<>();
protected GridPane gridPane;
protected double presentableWidth = 999;

public BaseCustomMenuPanel(MenuEditorFactory<Node> editorFactory,
ConditionalColoring conditionalColoring,
MenuTree menuTree,
boolean canClose) {
this.editorFactory = editorFactory;
this.conditionalColoring = conditionalColoring;
this.menuTree = menuTree;
this.panelCanBeClosed = canClose;
}

@Override
public Node getPanelToPresent(double width) throws Exception {
if (gridPane != null) {
// empty it if it already exists to make GC easier
gridPane.getChildren().clear();
gridPane.getColumnConstraints().clear();
gridPane.getRowConstraints().clear();
}
presentableWidth = width;
makeBasicGridLayout();
populateGrid();

return gridPane;
}
protected abstract void populateGrid();

protected void putIntoGrid(MenuItem item, ComponentSettings componentSettings) {
ComponentPositioning pos = componentSettings.getPosition();
var component = editorFactory.getComponentEditorItem(item, componentSettings, this::noAction);
component.ifPresent(comp -> {
controlsBeingManaged.put(item.getId(), comp);
gridPane.add(comp.createComponent(), pos.getCol(), pos.getRow(), pos.getColSpan(), pos.getRowSpan());
});
}

protected void makeBasicGridLayout() {
gridPane = new GridPane();
gridPane.setHgap(5);
gridPane.setVgap(5);
gridPane.setMaxWidth(9999);
gridPane.setPrefWidth(presentableWidth);
gridPane.getChildren().clear();

// the grid will be 4 across by three down.
gridPane.getColumnConstraints().clear();
gridPane.getRowConstraints().clear();
gridPane.setBackground(new Background(new BackgroundFill(
asFxColor(conditionalColoring.colorFor(NORMAL, TEXT_FIELD).getBg()), null, null
)));

}

protected void noAction(MenuItem menuItem) {
}


@Override
public boolean canClose() {
return panelCanBeClosed;
}


@Override
public void closePanel() {
controlsBeingManaged.clear();
}

@Override
public void itemHasUpdated(MenuItem item) {
if(controlsBeingManaged.containsKey(item.getId())) {
controlsBeingManaged.get(item.getId()).onItemUpdated(item, menuTree.getMenuState(item));
}
}

@Override
public void connectionIsUp(boolean isUp) {
gridPane.setDisable(!isUp);
}

@Override
public void acknowledgedCorrelationId(CorrelationId correlationId, AckStatus status) {
for(var component : controlsBeingManaged.values()) {
component.onCorrelation(correlationId, status);
}
}

@Override
public void tickAll() {
for(var component : controlsBeingManaged.values()) {
component.tick();
}
}
}

0 comments on commit 4678c15

Please sign in to comment.