Skip to content

Commit

Permalink
#537 documentation and a few minor updates.
Browse files Browse the repository at this point in the history
  • Loading branch information
davetcc committed Nov 15, 2024
1 parent b28c841 commit 609151b
Show file tree
Hide file tree
Showing 22 changed files with 316 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,21 @@

import java.util.Set;

/**
* The absolute base class of editor components, this class contains many helper functions needed to be able to render
* a menu item onto the display, regardless of the type of item, or display technology. This includes working out
* what text needs to be displayed, handling updates including the momentary change in color on value change. Error
* handling and correlation is also dealt with here.
*
* @param <W> The window component type
*/
/// This class is the base of all editor components that can represent a menu item onto a window, and as such contains
/// many helper functions needed to be able to keep the control in sync with the menu item, regardless of the type of
/// item, or display technology. Normally for JavaFX the generic type is `Node`.
///
/// In order to present a menu item onto the display, we need to work out what text needs to be displayed, what kind
/// of control is capable of presenting the menu item, and handling updates including a momentary change in color on
/// value change. Error handling and correlation is also dealt with here.
///
/// You normally create items of this class by interacting with the `MenuEditorFactory` interface. If you're trying
/// to create a custom page within `EmbedControl` it is recommended to start with `BaseCustomMenuPanel` as that already
/// creates everything you're likely to need, and has examples of creating controls.
///
/// @see MenuEditorFactory
/// @see com.thecoderscorner.embedcontrol.jfx.controlmgr.panels.BaseCustomMenuPanel
/// @param <W> The window component type for JavaFX it is Node.
public abstract class BaseEditorComponent<W> implements EditorComponent<W> {
public static final int MAX_CORRELATION_WAIT = 5000;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
import com.thecoderscorner.menu.domain.state.MenuState;
import com.thecoderscorner.menu.domain.util.MenuItemFormatter;

/// BaseUpDownIntEditorComponent is an abstract class that handles
/// the core logic for an editor component with up/down integer adjustments.
/// This class extends [BaseEditorComponent] and encapsulates the functionalities
/// required to manage integer values.
///
/// @param <T> The type of the value this editor component will manage.
/// @param <W> The type of the widget component.
public abstract class BaseUpDownIntEditorComponent<T, W> extends BaseEditorComponent<W> {
protected T currentVal;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.thecoderscorner.embedcontrol.core.controlmgr;

/**
* Represents an abstract way of positioning menu item controls for display in a grid. Containing the row, column
* and span of each.
*/
/// Represents an abstract way of positioning menu item controls for display in a grid. Containing the row, column
/// and span of each. Generally always used with {@link ComponentSettings}
public class ComponentPositioning {
private final int row;
private final int col;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
import static com.thecoderscorner.embedcontrol.customization.MenuFormItem.FONT_100_PERCENT;

/// This class describes how a menu item should be rendered onto the display. It contains the most important drawing
/// settings along with grid positioning data. It also allows for conditional colouring and custom drawing.
/// settings along with grid positioning data. It also allows for conditional colouring and custom drawing. Usually
/// use the {@link ComponentSettingsBuilder} in order to create an instance of this class.
///
/// For automatic menu layout cases, the layout is described in terms of font, color, position and justification using
/// this class based on some standard defaults.
///
/// @see CustomDrawingConfiguration
/// @see ConditionalColoring
public class ComponentSettings {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package com.thecoderscorner.embedcontrol.core.controlmgr;

import com.thecoderscorner.embedcontrol.core.controlmgr.color.ConditionalColoring;
import com.thecoderscorner.embedcontrol.customization.FontInformation;
import com.thecoderscorner.embedcontrol.customization.customdraw.CustomDrawingConfiguration;
import com.thecoderscorner.menu.domain.*;

import java.util.Set;

import static com.thecoderscorner.embedcontrol.core.controlmgr.EditorComponent.PortableAlignment;
import static com.thecoderscorner.embedcontrol.customization.MenuFormItem.FONT_100_PERCENT;

/**
* ComponentSettingsBuilder is a builder class for creating instances of ComponentSettings.
* It provides various methods to customize the settings of a component, such as font, colors,
* justification, position, control type, drawing mode, and custom drawing configuration.
*/
public class ComponentSettingsBuilder {
private final static Set<EditItemType> POSSIBLE_TIME_TYPES = Set.of(
EditItemType.TIME_12H,
EditItemType.TIME_24_HUNDREDS,
EditItemType.TIME_24H,
EditItemType.TIME_12H_HHMM,
EditItemType.TIME_24H_HHMM);

private MenuItem item;
private FontInformation fontInfo = FONT_100_PERCENT;
private ConditionalColoring colors;
private PortableAlignment justification = PortableAlignment.LEFT_VAL_RIGHT;
private ComponentPositioning position = new ComponentPositioning(0, 0);
private ControlType controlType = ControlType.TEXT_CONTROL;
private RedrawingMode drawMode = RedrawingMode.SHOW_NAME_VALUE;
private CustomDrawingConfiguration customDrawing = CustomDrawingConfiguration.NO_CUSTOM_DRAWING;

/// Create component settings builder object from a menu item. It defaults the fields to reasonable values as
/// much as possible by setting the font to 100% size, setting the control type to the default, and setting the
/// justification to the default too.
/// @param item the menu item to build for
/// @param color the colors to use for the control
public static ComponentSettingsBuilder forMenuItem(MenuItem item, ConditionalColoring color) {
var b = new ComponentSettingsBuilder();
b.colors = color;
b.item = item;
b.withControlType(defaultControlForType(item));
b.withJustification(defaultJustificationForType(b.controlType));
return b;
}

private static PortableAlignment defaultJustificationForType(ControlType controlType) {
return switch(controlType) {
case HORIZONTAL_SLIDER, UP_DOWN_CONTROL -> PortableAlignment.LEFT_VAL_RIGHT;
case BUTTON_CONTROL, VU_METER, ROTARY_METER -> PortableAlignment.CENTER;
default -> PortableAlignment.LEFT;
};
}

public static ControlType defaultControlForType(MenuItem item) {
return switch(item) {
case SubMenuItem _, BooleanMenuItem _, ActionMenuItem _ -> ControlType.BUTTON_CONTROL;
case AnalogMenuItem _ -> ControlType.HORIZONTAL_SLIDER;
case EnumMenuItem _, ScrollChoiceMenuItem _ -> ControlType.UP_DOWN_CONTROL;
case Rgb32MenuItem _ -> ControlType.RGB_CONTROL;
case RuntimeListMenuItem _ -> ControlType.LIST_CONTROL;
case CustomBuilderMenuItem _ -> ControlType.AUTH_IOT_CONTROL;
case EditableTextMenuItem txt when txt.getItemType() == EditItemType.GREGORIAN_DATE -> ControlType.DATE_CONTROL;
case EditableTextMenuItem txt when POSSIBLE_TIME_TYPES.contains(txt.getItemType()) -> ControlType.TIME_CONTROL;
default -> ControlType.TEXT_CONTROL;
};
}

/// Override the font from the default 100% size to another value
/// @param fontInfo the font to override with.
public ComponentSettingsBuilder withFont(FontInformation fontInfo) {
this.fontInfo = fontInfo;
return this;
}

/// Change the conditional coloring from the default one chosen.
/// @param colors the conditional colors
public ComponentSettingsBuilder withColors(ConditionalColoring colors) {
this.colors = colors;
return this;
}

/// Change the justification from the default value which is guessed during `forMenuItem` based on the control.
/// @param justification the justification to use
public ComponentSettingsBuilder withJustification(PortableAlignment justification) {
this.justification = justification;
return this;
}

/// Set the position of the control in the grid. Pretty much must always be set
/// @param position the position and span in the grid to create with
public ComponentSettingsBuilder withPosition(ComponentPositioning position) {
this.position = position;
return this;
}

/// Override the control type that was guessed during `forMenuItem`. You should be careful that the control type
/// you choose is compatible with the menu item type.
/// @param controlType the control type to use
/// @throws IllegalArgumentException if the control type is invalid for the menu item
public ComponentSettingsBuilder withControlType(ControlType controlType) {
if(!controlType.isSupportedFor(item)) {
throw new IllegalArgumentException("Control type %s cannot render %s".formatted(controlType, item.getClass().getSimpleName()));
}
this.controlType = controlType;
return this;
}

/// Sets the drawing mode for the item, defaults to show name and item.
/// @param drawMode the drawing mode
public ComponentSettingsBuilder withDrawMode(RedrawingMode drawMode) {
this.drawMode = drawMode;
return this;
}

/// Configure a custom drawing for the item, again make sure it is compatible with the menu type your using.
/// @param customDrawing the custom drawing to be used, must be compatible with the menu item type.
/// @throws IllegalArgumentException if the custom drawing is incompatible with the menu item type
public ComponentSettingsBuilder withCustomDrawing(CustomDrawingConfiguration customDrawing) {
if(!customDrawing.isSupportedFor(item)) {
throw new IllegalArgumentException("Custom drawing %s cannot render %s".formatted(customDrawing, item.getClass().getSimpleName()));
}
this.customDrawing = customDrawing;
return this;
}

/// Get the menuitem for this builder
/// @return menu item
public MenuItem getItem() {
return item;
}

/// Creates the component settings
/// @return the built object
public ComponentSettings build() {
return new ComponentSettings(colors, fontInfo, justification, position, drawMode, controlType, customDrawing, true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import java.util.List;

/**
* Represents the type of embedCONTROL control to use, these are hints to the rendering layer to help it to decide
* Represents the type of Embed Control display item to use, these are hints to the rendering layer to help it to decide
* what to display.
*/
public enum ControlType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@
import com.thecoderscorner.menu.remote.commands.AckStatus;
import com.thecoderscorner.menu.remote.protocol.CorrelationId;

/**
* This interface represents an item that can be drawn onto a display, it does not say what the control should be
* directly, the control is created by a call to createComponent, which generates the required UI node.
* @param <T> the base node type for the UI
*/
/// This interface represents an item that can be drawn onto a display, it does not say what the control should be
/// directly, the control is created by a call to createComponent, which generates the required UI node.
///
/// In order to present a menu item onto the display, we need to work out what text needs to be displayed, what kind
/// of control is capable of presenting the menu item, and handling updates including a momentary change in color on
/// value change. Error handling and correlation is also dealt with here.
///
/// You normally create items of this class by interacting with the `MenuEditorFactory` interface. If you're trying
/// to create a custom page within `EmbedControl` it is recommended to start with `BaseCustomMenuPanel` as that already
/// creates everything you're likely to need, and has examples of creating controls.
///
/// @see BaseEditorComponent
/// @param <T> the base node type for the UI
public interface EditorComponent<T> {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
import com.thecoderscorner.menu.remote.protocol.CorrelationId;

/**
* embedCONTROL is used in both local and remote settings, as such there is a need for different implementations of
* things such as updates, and connection handling. This interface bridges the gap between the two.
* Embed Control is used in both local and remote settings, as such there is a need for different implementations of
* things such as updates, and connection handling. This interface provides a way to provide a suitable implementation
* for all environments.
*/
public interface MenuComponentControl {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,19 @@
import java.util.Optional;
import java.util.function.Consumer;

/**
* A menu control grid is responsible for actually placing controls onto the UI. It has a method for each type of
* control that can be added, and has helper methods to clear down the grid, and to nest menu items, this allows for
* cases where recursive menu rendering is used to give visual clues such as indentation.
* @param <T>
*/
/// This factory is responsible for creating controls that can be placed into a window or screen. The default JavaFX
/// implementation creates JavaFX `Node` objects for each editor component. For the standard case that you want to
/// create a custom panel in Embed Control then you should most likely start with `BaseCustomMenuPanel`.
///
/// The factory has a method for each type of control that can be added. The `getComponentEditorItem` method takes a
/// `ComponentSettings` and returns an `EditorComponent`. The editor component is kind of like a wrapper around the
/// actual control, and can keep the control up-to-date and in the right state when associated with a
/// [com.thecoderscorner.embedcontrol.jfx.controlmgr.panels.BaseCustomMenuPanel]. If you want to use these components
/// outside of the base custom panel, then you should look at the custom panel implementation to see how to interact
/// with editor components.
///
/// @see com.thecoderscorner.embedcontrol.jfx.controlmgr.JfxMenuEditorFactory
/// @param <T> the control type
public interface MenuEditorFactory<T>
{
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import com.thecoderscorner.embedcontrol.core.controlmgr.color.ControlColor;
import com.thecoderscorner.embedcontrol.customization.*;
import com.thecoderscorner.embedcontrol.jfx.controlmgr.JfxNavigationManager;
import com.thecoderscorner.menu.domain.*;
import com.thecoderscorner.menu.domain.MenuItem;
import com.thecoderscorner.menu.domain.SubMenuItem;
import com.thecoderscorner.menu.domain.state.MenuTree;
import com.thecoderscorner.menu.domain.state.PortableColor;
import com.thecoderscorner.menu.domain.util.MenuItemHelper;
Expand All @@ -26,8 +27,9 @@
/// with a starting point and if the render is recursive, and then renders either all items in the current menu, or
/// everything from that point down.
///
/// It is an abstract class, in that the absolute methods by with the controls are put into the grid are implemented
/// elsewhere.
/// It is an abstract class, in that the absolute methods by which the controls are put into the grid are implemented
/// differently in each case. Normally you will not need to create these yourself as they are created automatically
/// when no custom panel exists.
/// @param <T> The type of UI component, normally Node
public abstract class MenuGridComponent<T> {
private final MenuItemStore menuItemStore;
Expand Down Expand Up @@ -115,19 +117,9 @@ public void itemHasUpdated(MenuItem item) {

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

return new ComponentSettings(new ScreenLayoutBasedConditionalColor(menuItemStore, position),
MenuFormItem.FONT_100_PERCENT, defaultJustificationForItem(Optional.of(item)),
position, defaultRedrawModeForItem(Optional.of(item)), defaultControlForType(item),
NO_CUSTOM_DRAWING, false);
}

protected RedrawingMode defaultRedrawModeForItem(Optional<MenuItem> item) {
return RedrawingMode.SHOW_NAME_VALUE;
}

protected EditorComponent.PortableAlignment defaultJustificationForItem(Optional<MenuItem> item) {
return EditorComponent.PortableAlignment.CENTER;
return ComponentSettingsBuilder.forMenuItem(item, new ScreenLayoutBasedConditionalColor(menuItemStore, position))
.withPosition(position)
.build();
}

protected ComponentPositioning defaultSpaceForItem(Optional<MenuItem> item) {
Expand Down Expand Up @@ -196,34 +188,4 @@ private ControlColor getControlColor(EditorComponent.RenderingStatus status, Col
}
}
}


public static ControlType defaultControlForType(MenuItem item) {
if (item instanceof SubMenuItem || item instanceof BooleanMenuItem || item instanceof ActionMenuItem) {
return ControlType.BUTTON_CONTROL;
} else if (item instanceof AnalogMenuItem) {
return ControlType.HORIZONTAL_SLIDER;
} else if (item instanceof Rgb32MenuItem) {
return ControlType.RGB_CONTROL;
} else if (item instanceof EnumMenuItem || item instanceof ScrollChoiceMenuItem) {
return ControlType.UP_DOWN_CONTROL;
} else if (item instanceof RuntimeListMenuItem) {
return ControlType.LIST_CONTROL;
} else if (item instanceof CustomBuilderMenuItem) {
return ControlType.AUTH_IOT_CONTROL;
} else if (item instanceof EditableTextMenuItem textItem) {
if (textItem.getItemType() == EditItemType.GREGORIAN_DATE) {
return ControlType.DATE_CONTROL;
} else if (textItem.getItemType() == EditItemType.TIME_24_HUNDREDS ||
textItem.getItemType() == EditItemType.TIME_12H ||
textItem.getItemType() == EditItemType.TIME_24H) {
return ControlType.TIME_CONTROL;
} else {
return ControlType.TEXT_CONTROL;
}
} else {
return ControlType.TEXT_CONTROL;
}
}

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
package com.thecoderscorner.embedcontrol.core.controlmgr;

/**
* Represents a panel that can be displayed onto a UI, it has a name, a UI representation and the possibility to close
* or remove it. In all cases they will be in a card like layout where panels are pushed onto the layout, and only
* one can be shown at once, either in a stack, or the panels may be in a selectable list.
* @param <T> the UI panel type, usually Node
*/
/// Represents a panel that can be displayed onto a UI, it has a name, a UI representation and the possibility to close
/// or remove it. Within Embed Control we use `PanelPresentable` in two ways. Firstly it is used in Embed Control UI
/// for a master-detail interface, where the created panels appear on the left, and upon clicking a panel, it presents
/// on the main area.
///
/// Secondly, it is used as a stack in the menu navigation flow when presenting a connection. In this case, if you
/// present a submenu at a time (non-recursive) then the panels will stack up, and a back button will appear at the top
/// of the connection panel.
///
/// In all cases will be in a card like layout where panels are pushed onto the layout, and only
/// one can be shown at once, either in a stack, or the panels may be in a selectable list.
///
/// @see com.thecoderscorner.embedcontrol.jfx.controlmgr.JfxNavigationHeader
/// @see NavigationManager
/// @param <T> the UI panel type, usually Node
public interface PanelPresentable<T> {
/**
* Gets the panel UI component for display, note that this can be called more than once
Expand Down
Loading

0 comments on commit 609151b

Please sign in to comment.