From 1efa9099ffebf73f3cb39ec293b2860cbd2603d7 Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Sat, 27 Sep 2025 16:38:51 -0500 Subject: [PATCH 01/14] Add fallback icon. --- .../src/libgdx/external/icons/fallback.svg | 52 ++++++++ .../ui/behavior/sequence/RDXFallbackNode.java | 11 ++ .../rdx/ui/widgets/ImGuiFallbackWidget.java | 113 ++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 ihmc-high-level-behaviors/src/libgdx/external/icons/fallback.svg create mode 100644 ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java diff --git a/ihmc-high-level-behaviors/src/libgdx/external/icons/fallback.svg b/ihmc-high-level-behaviors/src/libgdx/external/icons/fallback.svg new file mode 100644 index 000000000000..6daf83e0409f --- /dev/null +++ b/ihmc-high-level-behaviors/src/libgdx/external/icons/fallback.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java index 479e8a9e20a3..05f42f999c82 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java @@ -6,11 +6,13 @@ import us.ihmc.communication.crdt.CRDTInfo; import us.ihmc.rdx.imgui.ImGuiUniqueLabelMap; import us.ihmc.rdx.ui.behavior.tree.RDXBehaviorTreeNode; +import us.ihmc.rdx.ui.widgets.ImGuiFallbackWidget; import us.ihmc.tools.io.WorkspaceResourceDirectory; public class RDXFallbackNode extends RDXBehaviorTreeNode { private final ImGuiUniqueLabelMap labels = new ImGuiUniqueLabelMap(getClass()); + private final ImGuiFallbackWidget fallbackWidget = new ImGuiFallbackWidget(); public RDXFallbackNode(long id, CRDTInfo crdtInfo, WorkspaceResourceDirectory saveFileDirectory) { @@ -23,6 +25,15 @@ public void update() super.update(); } + @Override + public void renderTreeViewIconArea() + { + super.renderTreeViewIconArea(); + + fallbackWidget.render(ImGui.getFrameHeight()); + ImGui.sameLine(); + } + @Override public void renderContextMenuItems() { diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java new file mode 100644 index 000000000000..15fd9903a1b4 --- /dev/null +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java @@ -0,0 +1,113 @@ +package us.ihmc.rdx.ui.widgets; + +import imgui.ImGui; +import imgui.ImVec2; +import imgui.flag.ImGuiCol; +import us.ihmc.euclid.tuple2D.Point2D32; + +import java.util.ArrayList; + +/** + * Use ImGuiSVGWidgetNormalizer to edit. + */ +public class ImGuiFallbackWidget +{ + private final ArrayList topPart = new ArrayList<>(); + { + topPart.add(new Point2D32(0.106f, 0.497f)); + topPart.add(new Point2D32(0.176f, 0.157f)); + topPart.add(new Point2D32(0.332f, 0.070f)); + topPart.add(new Point2D32(0.419f, -0.104f)); + topPart.add(new Point2D32(0.388f, -0.318f)); + topPart.add(new Point2D32(0.110f, -0.500f)); + topPart.add(new Point2D32(-0.180f, -0.497f)); + topPart.add(new Point2D32(-0.347f, -0.355f)); + topPart.add(new Point2D32(-0.419f, -0.175f)); + topPart.add(new Point2D32(-0.249f, -0.094f)); + topPart.add(new Point2D32(-0.127f, -0.294f)); + topPart.add(new Point2D32(0.100f, -0.297f)); + topPart.add(new Point2D32(0.206f, -0.186f)); + topPart.add(new Point2D32(0.169f, -0.056f)); + topPart.add(new Point2D32(0.011f, 0.022f)); + topPart.add(new Point2D32(-0.134f, 0.172f)); + topPart.add(new Point2D32(-0.134f, 0.500f)); + topPart.add(new Point2D32(0.106f, 0.497f)); + } + private final ArrayList bottomPart = new ArrayList<>(); + { + float size = 0.16f; + float offset = 0.8f; + bottomPart.add(new Point2D32(-size, -size + offset)); + bottomPart.add(new Point2D32(size, -size + offset)); + bottomPart.add(new Point2D32(size, size + offset)); + bottomPart.add(new Point2D32(-size, size + offset)); + bottomPart.add(new Point2D32(-size, -size + offset)); + } + private final Point2D32 center = new Point2D32(); + private final ImVec2[] topPolygon = new ImVec2[topPart.size()]; + private final ImVec2[] bottomPolygon = new ImVec2[bottomPart.size()]; + private int lineColor; + + public ImGuiFallbackWidget() + { + for (int i = 0; i < topPolygon.length; i++) + topPolygon[i] = new ImVec2(); + for (int i = 0; i < bottomPolygon.length; i++) + bottomPolygon[i] = new ImVec2(); + } + + public void render(float lineHeight) + { + float fontSize = ImGui.getFontSize(); + + float scale = 0.7f; // Make parameter if desired + scale *= fontSize; + + center.set(0.4f * fontSize, 0.35f * fontSize); + + if (lineHeight == ImGui.getFrameHeight()) + center.addY(ImGui.getStyle().getFramePaddingY()); + + float xMin = Float.MAX_VALUE; + float xMax = Float.MIN_VALUE; + for (int i = 0; i < topPart.size(); i++) + { + topPolygon[i].set((topPart.get(i).getX32() * scale) + center.getX32(), (topPart.get(i).getY32() * scale) + center.getY32()); + + xMin = Math.min(xMin, topPolygon[i].x); + xMax = Math.max(xMax, topPolygon[i].x); + } + for (int i = 0; i < bottomPart.size(); i++) + { + bottomPolygon[i].set((bottomPart.get(i).getX32() * scale) + center.getX32(), (bottomPart.get(i).getY32() * scale) + center.getY32()); + + xMin = Math.min(xMin, bottomPolygon[i].x); + xMax = Math.max(xMax, bottomPolygon[i].x); + } + + float itemWidth = xMax - xMin; + + float cursorScreenPosX = ImGui.getCursorScreenPosX(); + float cursorScreenPosY = ImGui.getCursorScreenPosY(); + for (int i = 0; i < topPolygon.length; i++) + topPolygon[i].set(cursorScreenPosX + topPolygon[i].x, cursorScreenPosY + topPolygon[i].y); + for (int i = 0; i < bottomPolygon.length; i++) + bottomPolygon[i].set(cursorScreenPosX + bottomPolygon[i].x, cursorScreenPosY + bottomPolygon[i].y); + + lineColor = ImGui.getColorU32(ImGuiCol.Text); + + for (int i = 0; i < topPolygon.length - 1; i++) + drawLine(topPolygon[i].x, topPolygon[i].y, topPolygon[i + 1].x, topPolygon[i + 1].y); + for (int i = 0; i < bottomPolygon.length - 1; i++) + drawLine(bottomPolygon[i].x, bottomPolygon[i].y, bottomPolygon[i + 1].x, bottomPolygon[i + 1].y); + + ImGui.setCursorPosX(ImGui.getCursorPosX() + (itemWidth * 0.8f)); + + ImGui.newLine(); + } + + private void drawLine(float x0, float y0, float x1, float y1) + { + ImGui.getWindowDrawList().addLine(x0, y0, x1, y1, lineColor); + } +} From 025743795df50653827b0066b4d2f131802df56b Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Sun, 28 Sep 2025 17:59:46 -0500 Subject: [PATCH 02/14] Some cleanup and implement root icon widget. --- .../src/libgdx/external/icons/mockup.svg | 456 ++++++++++++++++++ .../rdx/ui/behavior/sequence/RDXLeafNode.java | 2 +- .../rdx/ui/behavior/tree/RDXBehaviorTree.java | 21 - .../ui/behavior/tree/RDXBehaviorTreeNode.java | 36 +- .../tree/RDXBehaviorTreeRootNode.java | 15 + .../ui/widgets}/ImGuiHollowArrowRenderer.java | 3 +- .../rdx/ui/widgets/ImGuiRootIconWidget.java | 56 +++ 7 files changed, 548 insertions(+), 41 deletions(-) create mode 100644 ihmc-high-level-behaviors/src/libgdx/external/icons/mockup.svg rename {ihmc-graphics/src/libgdx/java/us/ihmc/rdx/imgui => ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets}/ImGuiHollowArrowRenderer.java (98%) create mode 100644 ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java diff --git a/ihmc-high-level-behaviors/src/libgdx/external/icons/mockup.svg b/ihmc-high-level-behaviors/src/libgdx/external/icons/mockup.svg new file mode 100644 index 000000000000..23bae7cb5622 --- /dev/null +++ b/ihmc-high-level-behaviors/src/libgdx/external/icons/mockup.svg @@ -0,0 +1,456 @@ + + + +Root nodeSequenceCollapsed SequenceRight Arm ActionGripper ActionFallbackCheckpointGotoConditionLeft Arm ActionCollapsed Fallback diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java index a911a8853f9f..7ef27fc5c0d7 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java @@ -9,13 +9,13 @@ import us.ihmc.rdx.imgui.ImGuiExpandCollapseRenderer; import us.ihmc.rdx.imgui.ImGuiFlashingColors; import us.ihmc.rdx.imgui.ImGuiFlashingText; -import us.ihmc.rdx.imgui.ImGuiHollowArrowRenderer; import us.ihmc.rdx.imgui.ImGuiTools; import us.ihmc.rdx.imgui.ImGuiUniqueLabelMap; import us.ihmc.rdx.ui.RDXBaseUI; import us.ihmc.rdx.ui.behavior.tree.RDXBehaviorTreeNode; import us.ihmc.rdx.ui.behavior.tree.RDXBehaviorTreeRootNode; import us.ihmc.rdx.ui.behavior.tree.RDXBehaviorTreeTools; +import us.ihmc.rdx.ui.widgets.ImGuiHollowArrowRenderer; /** * RDX common implementation of all leaf nodes. diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTree.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTree.java index 97b6e9373f93..fb1396aa03c8 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTree.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTree.java @@ -38,7 +38,6 @@ public class RDXBehaviorTree extends BehaviorTree> private final ImGuiUniqueLabelMap labels = new ImGuiUniqueLabelMap(getClass()); private final RDXBehaviorTreeFileMenu fileMenu; private final RDXBehaviorTreeNodeCreationMenu nodeCreationMenu; - private final ImGuiExpandCollapseRenderer expandCollapseAllRenderer = new ImGuiExpandCollapseRenderer(); private final RDXBehaviorTreeWidgetsVerticalLayout treeWidgetsVerticalLayout; private boolean anyNodeSelected; private RDXBehaviorTreeNode selectedNode; @@ -184,16 +183,6 @@ protected void renderImGuiWidgetsPost() if (enableChildScrollableAreas) ImGui.beginChild(labels.get("Tree Explorer Scroll Area"), 0.0f, treeExplorerHeight); - if (expandCollapseAllRenderer.render(false, true)) - expandCollapseAll(true, rootNode); - if (expandCollapseAllRenderer.getIsHovered()) - ImGui.setTooltip("Expand all nodes"); - ImGui.sameLine(); - if (expandCollapseAllRenderer.render(true, true)) - expandCollapseAll(false, rootNode); - if (expandCollapseAllRenderer.getIsHovered()) - ImGui.setTooltip("Collapse all nodes"); - treeWidgetsVerticalLayout.renderImGuiWidgets(rootNode); boolean updatedEnableChildScrollableAreas; @@ -260,16 +249,6 @@ protected void renderImGuiWidgetsPost() modifyTreeTopology(); } - private void expandCollapseAll(boolean expandOrCollapse, RDXBehaviorTreeNode node) - { - node.setTreeWidgetExpanded(expandOrCollapse); - - for (RDXBehaviorTreeNode child : node.getChildren()) - { - expandCollapseAll(expandOrCollapse, child); - } - } - private void renderSelectedNodeSettingsWidgets(RDXBehaviorTreeNode node) { if (node.getSelected()) diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java index c0e6df7bc7eb..53d8a847da66 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java @@ -12,7 +12,6 @@ import us.ihmc.behaviors.behaviorTree.*; import us.ihmc.behaviors.behaviorTree.log.BehaviorTreeNodeMessageLogger.LogMessage; import us.ihmc.communication.crdt.CRDTInfo; -import us.ihmc.rdx.imgui.ImGuiExpandCollapseRenderer; import us.ihmc.rdx.imgui.ImGuiTools; import us.ihmc.rdx.imgui.ImGuiUniqueLabelMap; import us.ihmc.rdx.imgui.ImGuiVerticalAligner; @@ -48,7 +47,6 @@ public class RDXBehaviorTreeNode, private final ImBoolean selected = new ImBoolean(); private transient final ImVec2 lineMin = new ImVec2(); private transient final ImVec2 lineMax = new ImVec2(); - private final ImGuiExpandCollapseRenderer expandCollapseRenderer = new ImGuiExpandCollapseRenderer(); private boolean mouseHoveringNodeLine; private boolean anySpecificWidgetOnLineClicked = false; private boolean treeWidgetExpanded = false; @@ -132,22 +130,6 @@ public void renderGeneralRowBeginWidgets() { ImGui.getWindowDrawList().addRectFilled(lineMin.x, lineMin.y, lineMax.x, lineMax.y, ImGui.getColorU32(ImGuiCol.MenuBarBg)); } - - if (!getChildren().isEmpty()) - { - if (expandCollapseRenderer.render(treeWidgetExpanded, false, ImGui.getFrameHeight())) - { - anySpecificWidgetOnLineClicked = true; - treeWidgetExpanded = !treeWidgetExpanded; - } - ImGui.sameLine(); - } - else - { - // Add spacing to make up for expand collapse not being there - ImGui.setCursorPosX(ImGui.getCursorPosX() + ImGuiExpandCollapseRenderer.getPlaceholderWidth()); - treeWidgetExpanded = false; - } } public void renderTreeViewIconArea() @@ -221,6 +203,14 @@ public void renderContextMenuItems() ImGui.separator(); } + else + { + if (ImGui.menuItem(labels.get("Expand all nodes"))) // TODO: Maybe render the icon too + expandCollapseAll(true, this); + if (ImGui.menuItem(labels.get("Collapse all nodes"))) + expandCollapseAll(false, this); + ImGui.separator(); + } if (definition.isJSONRoot()) { @@ -248,6 +238,16 @@ else if (!isRootNode()) } } + private void expandCollapseAll(boolean expandOrCollapse, RDXBehaviorTreeNode node) + { + node.setTreeWidgetExpanded(expandOrCollapse); + + for (RDXBehaviorTreeNode child : node.getChildren()) + { + expandCollapseAll(expandOrCollapse, child); + } + } + public void renderNodeSettingsWidgets() { logArea.renderImGuiWidgets(); diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeRootNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeRootNode.java index 9178ab5b2d05..e45334158ed2 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeRootNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeRootNode.java @@ -13,6 +13,7 @@ import us.ihmc.rdx.ui.behavior.sequence.RDXActionNode; import us.ihmc.rdx.ui.behavior.sequence.RDXActionProgressWidgetsManager; import us.ihmc.rdx.ui.behavior.sequence.RDXLeafNode; +import us.ihmc.rdx.ui.widgets.ImGuiRootIconWidget; import us.ihmc.tools.io.WorkspaceResourceDirectory; import java.util.ArrayList; @@ -29,6 +30,7 @@ public class RDXBehaviorTreeRootNode extends RDXBehaviorTreeNode> nextForExecutionLeaves = new ArrayList<>(); private final List> currentlyExecutingLeaves = new ArrayList<>(); private final RDXActionProgressWidgetsManager progressWidgetsManager = new RDXActionProgressWidgetsManager(); + private final ImGuiRootIconWidget rootIconWidget = new ImGuiRootIconWidget(); public RDXBehaviorTreeRootNode(long id, CRDTInfo crdtInfo, WorkspaceResourceDirectory saveFileDirectory) { @@ -82,6 +84,19 @@ public void updateSubtree(RDXBehaviorTreeNode node) } } + @Override + public void renderTreeViewIconArea() + { + super.renderTreeViewIconArea(); + + if (rootIconWidget.render()) + { + setSpecificWidgetOnRowClicked(); + setTreeWidgetExpanded(!getTreeWidgetExpanded()); + } + ImGui.sameLine(); + } + @Override public void renderContextMenuItems() { diff --git a/ihmc-graphics/src/libgdx/java/us/ihmc/rdx/imgui/ImGuiHollowArrowRenderer.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiHollowArrowRenderer.java similarity index 98% rename from ihmc-graphics/src/libgdx/java/us/ihmc/rdx/imgui/ImGuiHollowArrowRenderer.java rename to ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiHollowArrowRenderer.java index ac7616378ab7..53341e93f64d 100644 --- a/ihmc-graphics/src/libgdx/java/us/ihmc/rdx/imgui/ImGuiHollowArrowRenderer.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiHollowArrowRenderer.java @@ -1,9 +1,10 @@ -package us.ihmc.rdx.imgui; +package us.ihmc.rdx.ui.widgets; import imgui.ImGui; import imgui.flag.ImGuiCol; import imgui.flag.ImGuiMouseButton; import us.ihmc.euclid.tuple2D.Point2D32; +import us.ihmc.rdx.imgui.ImGuiTools; public class ImGuiHollowArrowRenderer { diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java new file mode 100644 index 000000000000..f9d36ab7b68a --- /dev/null +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java @@ -0,0 +1,56 @@ +package us.ihmc.rdx.ui.widgets; + +import imgui.ImGui; +import imgui.flag.ImGuiCol; +import imgui.flag.ImGuiMouseButton; +import us.ihmc.rdx.imgui.ImGuiTools; + +/** + * A clickable behavior root node icon with hover state. + */ +public class ImGuiRootIconWidget +{ + public boolean render() + { + float lineHeight = ImGui.getFrameHeight(); + float fontSize = ImGui.getFontSize(); + float scale = 0.7f * fontSize; // Make parameter if desired + + float wide = 0.4f; + float down = 0.7f; + float circleSize = 0.21f; + float itemWidth = 2.0f * scale; + + float offsetX = ImGui.getCursorScreenPosX() + 0.8f * fontSize; + float offsetY = ImGui.getCursorScreenPosY() + 0.3f * fontSize; + if (lineHeight == ImGui.getFrameHeight()) + offsetY += ImGui.getStyle().getFramePaddingY(); + + boolean isHovered = ImGuiTools.isItemHovered(itemWidth, lineHeight); + if (isHovered) + { + int hoverColor = ImGui.getColorU32(ImGuiCol.ButtonHovered); + ImGui.getWindowDrawList().addCircleFilled(offsetX, offsetY, circleSize * scale, hoverColor); + ImGui.getWindowDrawList().addCircleFilled((-wide * scale) + offsetX, (down * scale) + offsetY, circleSize * scale, hoverColor); + ImGui.getWindowDrawList().addCircleFilled((wide * scale) + offsetX, (down * scale) + offsetY, circleSize * scale, hoverColor); + } + + float xShift = 0.03f; + float x1 = 0.13f; + float x2 = 0.34f; + float y1 = 0.15f; + float y2 = 0.5f; + int lineColor = ImGui.getColorU32(ImGuiCol.Text); + ImGui.getWindowDrawList() + .addLine(((x1 - xShift) * scale) + offsetX, (y1 * scale) + offsetY, ((x2 - xShift) * scale) + offsetX, (y2 * scale) + offsetY, lineColor); + ImGui.getWindowDrawList() + .addLine(((-x1 - xShift) * scale) + offsetX, (y1 * scale) + offsetY, ((-x2 - xShift) * scale) + offsetX, (y2 * scale) + offsetY, lineColor); + ImGui.getWindowDrawList().addCircle(offsetX, offsetY, circleSize * scale, lineColor); + ImGui.getWindowDrawList().addCircle((-wide * scale) + offsetX, (down * scale) + offsetY, circleSize * scale, lineColor); + ImGui.getWindowDrawList().addCircle((wide * scale) + offsetX, (down * scale) + offsetY, circleSize * scale, lineColor); + + ImGui.setCursorPosX(ImGui.getCursorPosX() + itemWidth); + ImGui.newLine(); + return isHovered && ImGui.isMouseClicked(ImGuiMouseButton.Left); + } +} From a374d295a9189dd3fa4c7167845a4bc3324fbdf0 Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Sun, 28 Sep 2025 21:33:20 -0500 Subject: [PATCH 03/14] Add sequence icon. Work in progress. --- .../behavior/sequence/RDXActionSequence.java | 15 ++ .../RDXBehaviorTreeWidgetsVerticalLayout.java | 2 +- .../rdx/ui/widgets/ImGuiFallbackWidget.java | 2 +- .../rdx/ui/widgets/ImGuiRootIconWidget.java | 2 +- .../ui/widgets/ImGuiSequenceIconWidget.java | 151 ++++++++++++++++++ 5 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSequenceIconWidget.java diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXActionSequence.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXActionSequence.java index 109862b6d268..4430adb601a3 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXActionSequence.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXActionSequence.java @@ -6,11 +6,13 @@ import us.ihmc.communication.crdt.CRDTInfo; import us.ihmc.rdx.imgui.ImGuiUniqueLabelMap; import us.ihmc.rdx.ui.behavior.tree.RDXBehaviorTreeNode; +import us.ihmc.rdx.ui.widgets.ImGuiSequenceIconWidget; import us.ihmc.tools.io.WorkspaceResourceDirectory; public class RDXActionSequence extends RDXBehaviorTreeNode { private final ImGuiUniqueLabelMap labels = new ImGuiUniqueLabelMap(getClass()); + private final ImGuiSequenceIconWidget sequenceIconWidget = new ImGuiSequenceIconWidget(); public RDXActionSequence(long id, CRDTInfo crdtInfo, WorkspaceResourceDirectory saveFileDirectory) { @@ -23,6 +25,19 @@ public void update() super.update(); } + @Override + public void renderTreeViewIconArea() + { + super.renderTreeViewIconArea(); + + if (sequenceIconWidget.render()) + { + setSpecificWidgetOnRowClicked(); + setTreeWidgetExpanded(!getTreeWidgetExpanded()); + } + ImGui.sameLine(); + } + @Override public void renderContextMenuItems() { diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java index c3353052fba5..c9e1907c02cf 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java @@ -115,7 +115,7 @@ public void renderImGuiWidgets(RDXBehaviorTreeNode node) if (node.getTreeWidgetExpanded()) { - float indentAmount = 10.0f; + float indentAmount = ImGui.getFontSize() * 0.5f; ImGui.indent(indentAmount); for (RDXBehaviorTreeNode child : node.getChildren()) diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java index 15fd9903a1b4..34f509e3f84d 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java @@ -63,7 +63,7 @@ public void render(float lineHeight) float scale = 0.7f; // Make parameter if desired scale *= fontSize; - center.set(0.4f * fontSize, 0.35f * fontSize); + center.set(0.6f * fontSize, 0.35f * fontSize); if (lineHeight == ImGui.getFrameHeight()) center.addY(ImGui.getStyle().getFramePaddingY()); diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java index f9d36ab7b68a..bfaa78e51b27 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java @@ -14,7 +14,7 @@ public boolean render() { float lineHeight = ImGui.getFrameHeight(); float fontSize = ImGui.getFontSize(); - float scale = 0.7f * fontSize; // Make parameter if desired + float scale = 0.7f * fontSize; float wide = 0.4f; float down = 0.7f; diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSequenceIconWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSequenceIconWidget.java new file mode 100644 index 000000000000..98180d79258d --- /dev/null +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSequenceIconWidget.java @@ -0,0 +1,151 @@ +package us.ihmc.rdx.ui.widgets; + +import imgui.ImGui; +import imgui.flag.ImGuiCol; +import imgui.flag.ImGuiMouseButton; +import us.ihmc.rdx.imgui.ImGuiTools; + +/** + * A clickable behavior root node icon with hover state. + */ +public class ImGuiSequenceIconWidget +{ + public boolean render() + { + float lineHeight = ImGui.getFrameHeight(); + float fontSize = ImGui.getFontSize(); + float scale = 0.7f * fontSize; + + float offsetX = ImGui.getCursorScreenPosX() + 1.0f * fontSize; + float offsetY = ImGui.getCursorScreenPosY() + 0.41f * fontSize; + if (lineHeight == ImGui.getFrameHeight()) + offsetY += ImGui.getStyle().getFramePaddingY(); + + float squish = 0.5f; + float zero = 0.0f; + float arrowheadHalfheight = 0.4f * squish; + float arrowheadHalfwidth = 0.6f; + float baseWidth = 0.9f; + float baseHeight = 0.35f * squish; + float itemWidth = 2.3f * scale; + int lineColor = ImGui.getColorU32(ImGuiCol.Text); + + boolean isHovered = ImGuiTools.isItemHovered(itemWidth, lineHeight); + + float arrowheadTopX = zero; + float arrowheadTopY = -arrowheadHalfheight; + float arrowheadTipX = arrowheadHalfwidth; + float arrowheadTipY = zero; + float arrowheadBottomX = zero; + float arrowheadBottomY = arrowheadHalfheight; + float baseTopLeftX = -baseWidth; + float baseTopLeftY = -baseHeight / 2.0f; + float baseTopRightX = zero; + float baseTopRightY = -baseHeight / 2.0f; + float baseBottomLeftX = -baseWidth; + float baseBottomLeftY = baseHeight / 2.0f; + float baseBottomRightX = zero; + float baseBottomRightY = baseHeight / 2.0f; + + if (isHovered) + { + int hoverColor = ImGui.getColorU32(ImGuiCol.ButtonHovered); + + ImGui.getWindowDrawList() .addTriangleFilled(offsetX + scale * arrowheadTopX, offsetY + scale * arrowheadTopY, + offsetX + scale * arrowheadTipX, offsetY + scale * arrowheadTipY, + offsetX + scale * arrowheadBottomX, offsetY + scale * arrowheadBottomY, + hoverColor); + ImGui.getWindowDrawList() .addRectFilled(offsetX + scale * baseTopLeftX, offsetY + scale * baseTopLeftY, + offsetX + scale * baseBottomRightX + 1.0f, offsetY + scale * baseBottomRightY + 1.0f, + hoverColor); + } + + ImGui.getWindowDrawList().addLine(offsetX + scale * arrowheadTopX, + offsetY + scale * arrowheadTopY, + offsetX + scale * arrowheadTipX, + offsetY + scale * arrowheadTipY, + lineColor); + ImGui.getWindowDrawList().addLine(offsetX + scale * arrowheadTipX, + offsetY + scale * arrowheadTipY, + offsetX + scale * arrowheadBottomX, + offsetY + scale * arrowheadBottomY, + lineColor); + ImGui.getWindowDrawList().addLine(offsetX + scale * arrowheadTopX, + offsetY + scale * arrowheadTopY, + offsetX + scale * baseTopRightX, + offsetY + scale * baseTopRightY, + lineColor); + ImGui.getWindowDrawList().addLine(offsetX + scale * arrowheadBottomX, + offsetY + scale * arrowheadBottomY, + offsetX + scale * baseBottomRightX, + offsetY + scale * baseBottomRightY, + lineColor); + ImGui.getWindowDrawList().addLine(offsetX + scale * baseTopRightX, + offsetY + scale * baseTopRightY, + offsetX + scale * baseTopLeftX, + offsetY + scale * baseTopLeftY, + lineColor); + ImGui.getWindowDrawList().addLine(offsetX + scale * baseTopLeftX, + offsetY + scale * baseTopLeftY, + offsetX + scale * baseBottomLeftX, + offsetY + scale * baseBottomLeftY, + lineColor); + ImGui.getWindowDrawList().addLine(offsetX + scale * baseBottomLeftX, + offsetY + scale * baseBottomLeftY, + offsetX + scale * baseBottomRightX, + offsetY + scale * baseBottomRightY, + lineColor); + offsetY += 0.5f * scale; + if (isHovered) + { + int hoverColor = ImGui.getColorU32(ImGuiCol.ButtonHovered); + + ImGui.getWindowDrawList() .addTriangleFilled(offsetX + scale * arrowheadTopX, offsetY + scale * arrowheadTopY, + offsetX + scale * arrowheadTipX, offsetY + scale * arrowheadTipY, + offsetX + scale * arrowheadBottomX, offsetY + scale * arrowheadBottomY, + hoverColor); + ImGui.getWindowDrawList() .addRectFilled(offsetX + scale * baseTopLeftX, offsetY + scale * baseTopLeftY, + offsetX + scale * baseBottomRightX + 1.0f, offsetY + scale * baseBottomRightY + 1.0f, + hoverColor); + } + ImGui.getWindowDrawList().addLine(offsetX + scale * arrowheadTopX, + offsetY + scale * arrowheadTopY, + offsetX + scale * arrowheadTipX, + offsetY + scale * arrowheadTipY, + lineColor); + ImGui.getWindowDrawList().addLine(offsetX + scale * arrowheadTipX, + offsetY + scale * arrowheadTipY, + offsetX + scale * arrowheadBottomX, + offsetY + scale * arrowheadBottomY, + lineColor); + ImGui.getWindowDrawList().addLine(offsetX + scale * arrowheadTopX, + offsetY + scale * arrowheadTopY, + offsetX + scale * baseTopRightX, + offsetY + scale * baseTopRightY, + lineColor); + ImGui.getWindowDrawList().addLine(offsetX + scale * arrowheadBottomX, + offsetY + scale * arrowheadBottomY, + offsetX + scale * baseBottomRightX, + offsetY + scale * baseBottomRightY, + lineColor); + ImGui.getWindowDrawList().addLine(offsetX + scale * baseTopRightX, + offsetY + scale * baseTopRightY, + offsetX + scale * baseTopLeftX, + offsetY + scale * baseTopLeftY, + lineColor); + ImGui.getWindowDrawList().addLine(offsetX + scale * baseTopLeftX, + offsetY + scale * baseTopLeftY, + offsetX + scale * baseBottomLeftX, + offsetY + scale * baseBottomLeftY, + lineColor); + ImGui.getWindowDrawList().addLine(offsetX + scale * baseBottomLeftX, + offsetY + scale * baseBottomLeftY, + offsetX + scale * baseBottomRightX, + offsetY + scale * baseBottomRightY, + lineColor); + + ImGui.setCursorPosX(ImGui.getCursorPosX() + itemWidth); + ImGui.newLine(); + return isHovered && ImGui.isMouseClicked(ImGuiMouseButton.Left); + } +} From c84e4b4eddec488986fc0913324a50478be0180c Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Sat, 4 Oct 2025 21:30:03 -0500 Subject: [PATCH 04/14] Add collapsed arrow indicator. Fix various widget rendering issues. --- .../rdx/ui/behavior/sequence/RDXFallbackNode.java | 7 ++++++- .../ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java | 12 +++++------- .../rdx/ui/behavior/tree/RDXBehaviorTreeNode.java | 11 +++++++++++ .../us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java | 11 ++++++++--- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java index 05f42f999c82..e8fefa4114a2 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java @@ -30,7 +30,12 @@ public void renderTreeViewIconArea() { super.renderTreeViewIconArea(); - fallbackWidget.render(ImGui.getFrameHeight()); + ImGui.setCursorPosX(ImGui.getCursorPosX() + ImGui.getStyle().getItemSpacingX()); + if (fallbackWidget.render()) + { + setSpecificWidgetOnRowClicked(); + setTreeWidgetExpanded(!getTreeWidgetExpanded()); + } ImGui.sameLine(); } diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java index 7ef27fc5c0d7..21a955d54f8a 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java @@ -54,21 +54,19 @@ public void renderTreeViewIconArea() RDXBehaviorTreeRootNode actionSequence = RDXBehaviorTreeTools.findRootNode(this); if (actionSequence != null) { - // The arrow can move back to take the place of the expand collapse, since this is a leaf node - ImGui.setCursorPosX(ImGui.getCursorPosX() - ImGuiExpandCollapseRenderer.getPlaceholderWidth()); + // Give the arrow a little space to the left, like the other icons + ImGui.setCursorPosX(ImGui.getCursorPosX() + ImGui.getStyle().getItemSpacingX()); // Not displaying this now until we calculate it correctly. @dcalvert if (state.getConcurrencyRank() != 1) { ImGui.pushStyleColor(ImGuiCol.Text, ImGui.getColorU32(ImGuiCol.TextDisabled)); - ImGui.text(state.getConcurrencyRank() == 1 ? " " : String.valueOf(state.getConcurrencyRank())); + String text = state.getConcurrencyRank() == 1 ? " " : String.valueOf(state.getConcurrencyRank()); + ImGui.setCursorPosX(ImGui.getCursorPosX() - ImGuiTools.calcTextSizeX(text) - ImGui.getStyle().getItemSpacingX()); + ImGui.text(text); ImGui.popStyleColor(); ImGui.sameLine(); } - else - { - ImGui.setCursorPosX(ImGui.getCursorPosX() + ImGuiTools.calcTextSizeX("2") + ImGui.getStyle().getItemSpacingX()); - } boolean colorArrow = state.getIsNextForExecution() || state.getIsExecuting(); int arrowColor = state.getIsNextForExecution() ? ImGuiTools.GREEN : isExecutingFlashingColor.getColor(state.getIsExecuting()); diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java index 53d8a847da66..45cee4ed58db 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java @@ -130,6 +130,17 @@ public void renderGeneralRowBeginWidgets() { ImGui.getWindowDrawList().addRectFilled(lineMin.x, lineMin.y, lineMax.x, lineMax.y, ImGui.getColorU32(ImGuiCol.MenuBarBg)); } + + if (!getChildren().isEmpty() && !treeWidgetExpanded) + { + float offsetX = ImGui.getCursorScreenPosX(); + float offsetY = ImGui.getCursorScreenPosY() + ImGui.getFrameHeight() * 0.2f; + float width = ImGui.getFontSize() / 2.5f; + float height = ImGui.getFrameHeight() * 0.5f; + int color = ImGui.getColorU32(ImGuiCol.Text); + ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY + height, color); + ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY, color); + } } public void renderTreeViewIconArea() diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java index 34f509e3f84d..3afa334de8ec 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java @@ -3,7 +3,9 @@ import imgui.ImGui; import imgui.ImVec2; import imgui.flag.ImGuiCol; +import imgui.flag.ImGuiMouseButton; import us.ihmc.euclid.tuple2D.Point2D32; +import us.ihmc.rdx.imgui.ImGuiTools; import java.util.ArrayList; @@ -56,14 +58,15 @@ public ImGuiFallbackWidget() bottomPolygon[i] = new ImVec2(); } - public void render(float lineHeight) + public boolean render() { + float lineHeight = ImGui.getFrameHeight(); float fontSize = ImGui.getFontSize(); float scale = 0.7f; // Make parameter if desired scale *= fontSize; - center.set(0.6f * fontSize, 0.35f * fontSize); + center.set(0.3f * fontSize, 0.35f * fontSize); if (lineHeight == ImGui.getFrameHeight()) center.addY(ImGui.getStyle().getFramePaddingY()); @@ -86,6 +89,7 @@ public void render(float lineHeight) } float itemWidth = xMax - xMin; + boolean isHovered = ImGuiTools.isItemHovered(itemWidth, lineHeight); float cursorScreenPosX = ImGui.getCursorScreenPosX(); float cursorScreenPosY = ImGui.getCursorScreenPosY(); @@ -94,7 +98,7 @@ public void render(float lineHeight) for (int i = 0; i < bottomPolygon.length; i++) bottomPolygon[i].set(cursorScreenPosX + bottomPolygon[i].x, cursorScreenPosY + bottomPolygon[i].y); - lineColor = ImGui.getColorU32(ImGuiCol.Text); + lineColor = isHovered ? ImGui.getColorU32(ImGuiCol.ButtonHovered) : ImGui.getColorU32(ImGuiCol.Text); // TODO: Need to fill instead for (int i = 0; i < topPolygon.length - 1; i++) drawLine(topPolygon[i].x, topPolygon[i].y, topPolygon[i + 1].x, topPolygon[i + 1].y); @@ -104,6 +108,7 @@ public void render(float lineHeight) ImGui.setCursorPosX(ImGui.getCursorPosX() + (itemWidth * 0.8f)); ImGui.newLine(); + return isHovered && ImGui.isMouseClicked(ImGuiMouseButton.Left); } private void drawLine(float x0, float y0, float x1, float y1) From 1e7399d8cd03e02ae47550876585e6f349e6c504 Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Sat, 4 Oct 2025 21:51:54 -0500 Subject: [PATCH 05/14] Highlight fallback icon. --- .../rdx/ui/widgets/ImGuiFallbackWidget.java | 52 +++++++++++++++---- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java index 3afa334de8ec..a730e1431cf6 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java @@ -48,7 +48,6 @@ public class ImGuiFallbackWidget private final Point2D32 center = new Point2D32(); private final ImVec2[] topPolygon = new ImVec2[topPart.size()]; private final ImVec2[] bottomPolygon = new ImVec2[bottomPart.size()]; - private int lineColor; public ImGuiFallbackWidget() { @@ -98,21 +97,56 @@ public boolean render() for (int i = 0; i < bottomPolygon.length; i++) bottomPolygon[i].set(cursorScreenPosX + bottomPolygon[i].x, cursorScreenPosY + bottomPolygon[i].y); - lineColor = isHovered ? ImGui.getColorU32(ImGuiCol.ButtonHovered) : ImGui.getColorU32(ImGuiCol.Text); // TODO: Need to fill instead + int hoverColor = ImGui.getColorU32(ImGuiCol.ButtonHovered); + int lineColor = ImGui.getColorU32(ImGuiCol.Text); + + if (isHovered) + { + int offset = 14; + int size = 6; + ImVec2[] subpoly = new ImVec2[size]; + for (int i = 0; i < size; i++) + subpoly[i] = topPolygon[(i + offset) % topPolygon.length]; + ImGui.getWindowDrawList().addConvexPolyFilled(subpoly, subpoly.length, hoverColor); + + offset = 5; + for (int i = 0; i < size; i++) + subpoly[i] = topPolygon[(i + offset) % topPolygon.length]; + ImGui.getWindowDrawList().addConvexPolyFilled(subpoly, subpoly.length, hoverColor); + + subpoly = new ImVec2[4]; + subpoly[0] = topPolygon[13]; + subpoly[1] = topPolygon[14]; + subpoly[2] = topPolygon[1]; + subpoly[3] = topPolygon[2]; + ImGui.getWindowDrawList().addConvexPolyFilled(subpoly, subpoly.length, hoverColor); + subpoly[0] = topPolygon[12]; + subpoly[1] = topPolygon[13]; + subpoly[2] = topPolygon[2]; + subpoly[3] = topPolygon[3]; + ImGui.getWindowDrawList().addConvexPolyFilled(subpoly, subpoly.length, hoverColor); + subpoly[0] = topPolygon[11]; + subpoly[1] = topPolygon[12]; + subpoly[2] = topPolygon[3]; + subpoly[3] = topPolygon[4]; + ImGui.getWindowDrawList().addConvexPolyFilled(subpoly, subpoly.length, hoverColor); + subpoly[0] = topPolygon[10]; + subpoly[1] = topPolygon[11]; + subpoly[2] = topPolygon[4]; + subpoly[3] = topPolygon[5]; + ImGui.getWindowDrawList().addConvexPolyFilled(subpoly, subpoly.length, hoverColor); + + ImGui.getWindowDrawList().addRectFilled(bottomPolygon[0].x, bottomPolygon[0].y, bottomPolygon[2].x, bottomPolygon[2].y, hoverColor); + } for (int i = 0; i < topPolygon.length - 1; i++) - drawLine(topPolygon[i].x, topPolygon[i].y, topPolygon[i + 1].x, topPolygon[i + 1].y); + ImGui.getWindowDrawList().addLine(topPolygon[i].x, topPolygon[i].y, topPolygon[i + 1].x, topPolygon[i + 1].y, lineColor); for (int i = 0; i < bottomPolygon.length - 1; i++) - drawLine(bottomPolygon[i].x, bottomPolygon[i].y, bottomPolygon[i + 1].x, bottomPolygon[i + 1].y); + ImGui.getWindowDrawList().addLine(bottomPolygon[i].x, bottomPolygon[i].y, bottomPolygon[i + 1].x, bottomPolygon[i + 1].y, lineColor); ImGui.setCursorPosX(ImGui.getCursorPosX() + (itemWidth * 0.8f)); ImGui.newLine(); return isHovered && ImGui.isMouseClicked(ImGuiMouseButton.Left); } - - private void drawLine(float x0, float y0, float x1, float y1) - { - ImGui.getWindowDrawList().addLine(x0, y0, x1, y1, lineColor); - } } From 930326fb2f8b2b07767b877f05ebd7f999cdecdd Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Sat, 4 Oct 2025 23:40:17 -0500 Subject: [PATCH 06/14] idk --- .../us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java | 1 + .../behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java index e8fefa4114a2..a53ebd4c5c2b 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java @@ -37,6 +37,7 @@ public void renderTreeViewIconArea() setTreeWidgetExpanded(!getTreeWidgetExpanded()); } ImGui.sameLine(); + ImGui.setCursorPosX(ImGui.getCursorPosX() + ImGui.getStyle().getItemSpacingX()); } @Override diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java index c9e1907c02cf..2161698e5a72 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java @@ -35,8 +35,8 @@ public void renderImGuiWidgets(RDXBehaviorTreeNode node) node.renderGeneralRowBeginWidgets(); node.renderTreeViewIconArea(); - if (node.getParent() != null) - node.getParent().getChildrenDescriptionAligner().align(); +// if (node.getParent() != null) +// node.getParent().getChildrenDescriptionAligner().align(); node.renderNodeName(); ImGui.popStyleVar(); From 7fb1f709bce031f91b5727fb9288d0fd2f4cbc80 Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Sun, 5 Oct 2025 22:02:23 -0500 Subject: [PATCH 07/14] Draw condition node widget. --- .../src/libgdx/external/icons/condition.svg | 61 ++++++++++++ .../ui/behavior/logic/RDXConditionNode.java | 11 +++ .../ui/widgets/ImGuiConditionNodeWidget.java | 99 +++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 ihmc-high-level-behaviors/src/libgdx/external/icons/condition.svg create mode 100644 ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiConditionNodeWidget.java diff --git a/ihmc-high-level-behaviors/src/libgdx/external/icons/condition.svg b/ihmc-high-level-behaviors/src/libgdx/external/icons/condition.svg new file mode 100644 index 000000000000..77a8382dfd83 --- /dev/null +++ b/ihmc-high-level-behaviors/src/libgdx/external/icons/condition.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXConditionNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXConditionNode.java index 662457a78aa4..1a398caac9bb 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXConditionNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXConditionNode.java @@ -10,12 +10,14 @@ import us.ihmc.rdx.ui.behavior.logic.condition.RDXLLMCondition; import us.ihmc.rdx.ui.behavior.logic.condition.RDXProximityCondition; import us.ihmc.rdx.ui.behavior.sequence.RDXLeafNode; +import us.ihmc.rdx.ui.widgets.ImGuiConditionNodeWidget; import us.ihmc.robotics.referenceFrames.ReferenceFrameLibrary; import us.ihmc.tools.io.WorkspaceResourceDirectory; public class RDXConditionNode extends RDXLeafNode { private final ImGuiUniqueLabelMap labels = new ImGuiUniqueLabelMap(getClass()); + private final ImGuiConditionNodeWidget conditionIconWidget = new ImGuiConditionNodeWidget(); private final RDXCounterCondition counter; private final RDXLLMCondition llm; @@ -30,6 +32,15 @@ public RDXConditionNode(long id, CRDTInfo crdtInfo, WorkspaceResourceDirectory s proximityCheck = new RDXProximityCondition(state, referenceFrameLibrary); } + @Override + public void renderTreeViewIconArea() + { + super.renderTreeViewIconArea(); + + conditionIconWidget.render(); + ImGui.sameLine(); + } + @Override protected void renderImGuiWidgetsInternal() { diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiConditionNodeWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiConditionNodeWidget.java new file mode 100644 index 000000000000..7811db3f2949 --- /dev/null +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiConditionNodeWidget.java @@ -0,0 +1,99 @@ +package us.ihmc.rdx.ui.widgets; + +import imgui.ImGui; +import imgui.ImVec2; +import imgui.flag.ImGuiCol; +import imgui.flag.ImGuiMouseButton; +import us.ihmc.euclid.tuple2D.Point2D32; +import us.ihmc.rdx.imgui.ImGuiTools; + +import java.util.ArrayList; + +/** + * A clickable behavior root node icon with hover state. + */ +public class ImGuiConditionNodeWidget +{ + private final ArrayList checkmark = new ArrayList<>(); + { + checkmark.add(new Point2D32(-0.143f, 0.405f)); + checkmark.add(new Point2D32(0.500f, -0.285f)); + checkmark.add(new Point2D32(0.375f, -0.405f)); + checkmark.add(new Point2D32(-0.157f, 0.146f)); + checkmark.add(new Point2D32(-0.368f, -0.056f)); + checkmark.add(new Point2D32(-0.500f, 0.103f)); + checkmark.add(new Point2D32(-0.143f, 0.405f)); + } + private final ImVec2[] checkPolygon = new ImVec2[checkmark.size()]; + private final ImVec2[] xPolygon = new ImVec2[13]; + + public ImGuiConditionNodeWidget() + { + for (int i = 0; i < checkPolygon.length; i++) + checkPolygon[i] = new ImVec2(); + for (int i = 0; i < xPolygon.length; i++) + xPolygon[i] = new ImVec2(); + } + + public boolean render() + { + float lineHeight = ImGui.getFrameHeight(); + float fontSize = ImGui.getFontSize(); + float scale = 0.5f * fontSize; + + float offsetX = ImGui.getCursorScreenPosX() + 0.3f * fontSize; + float offsetY = ImGui.getCursorScreenPosY() + 0.41f * fontSize; + if (lineHeight == ImGui.getFrameHeight()) + offsetY += ImGui.getStyle().getFramePaddingY(); + + int lineColor = ImGui.getColorU32(ImGuiCol.Text); + + float xMin = Float.MAX_VALUE; + float xMax = Float.MIN_VALUE; + for (int i = 0; i < checkmark.size(); i++) + { + checkPolygon[i].set((checkmark.get(i).getX32() * scale) + offsetX, (checkmark.get(i).getY32() * scale) + offsetY); + + xMin = Math.min(xMin, checkPolygon[i].x); + xMax = Math.max(xMax, checkPolygon[i].x); + } + + xPolygon[0].set(-0.185f, -0.003f); + xPolygon[1].set(-0.495f, 0.313f); + xPolygon[2].set(-0.325f, 0.500f); + xPolygon[3].set(-0.008f, 0.165f); + xPolygon[4].set(0.314f, 0.479f); + xPolygon[5].set(0.495f, 0.271f); + xPolygon[6].set(0.187f, -0.018f); + xPolygon[7].set(0.483f, -0.321f); + xPolygon[8].set(0.317f, -0.500f); + xPolygon[9].set(-0.001f, -0.181f); + xPolygon[10].set(-0.316f, -0.495f); + xPolygon[11].set(-0.495f, -0.271f); + xPolygon[12].set(-0.185f, -0.003f); + + offsetX += 0.5f * fontSize; + offsetY += 0.3f * fontSize; + scale = 0.4f * fontSize; + for (int i = 0; i < xPolygon.length; i++) + { + xPolygon[i].set((xPolygon[i].x * scale) + offsetX, (xPolygon[i].y * scale) + offsetY); + + xMin = Math.min(xMin, xPolygon[i].x); + xMax = Math.max(xMax, xPolygon[i].x); + } + + float itemWidth = xMax - xMin; + boolean isHovered = ImGuiTools.isItemHovered(itemWidth, lineHeight); + + for (int i = 0; i < checkPolygon.length - 1; i++) + ImGui.getWindowDrawList().addLine(checkPolygon[i].x, checkPolygon[i].y, checkPolygon[i + 1].x, checkPolygon[i + 1].y, lineColor); + for (int i = 0; i < xPolygon.length - 1; i++) + ImGui.getWindowDrawList().addLine(xPolygon[i].x, xPolygon[i].y, xPolygon[i + 1].x, xPolygon[i + 1].y, lineColor); + ImGui.getWindowDrawList().addLine(-1.6f * scale + offsetX, 0.5f * scale + offsetY, 0.1f * scale + offsetX, -1.3f * scale + offsetY, lineColor); + + ImGui.setCursorPosX(ImGui.getCursorPosX() + itemWidth); + ImGui.newLine(); + return isHovered && ImGui.isMouseClicked(ImGuiMouseButton.Left); + } +} From 55c883060dff3824442625b0f58ef660b4f92ccc Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Thu, 16 Oct 2025 15:50:58 -0500 Subject: [PATCH 08/14] Add arm icon. --- .../behavior/actions/RDXHandPoseAction.java | 8 +-- .../rdx/ui/widgets/ImGuiArmIconWidget.java | 71 +++++++++++++++++++ 2 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiArmIconWidget.java diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXHandPoseAction.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXHandPoseAction.java index 5a0239a7bc1d..5a7839b119e5 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXHandPoseAction.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXHandPoseAction.java @@ -33,7 +33,7 @@ import us.ihmc.rdx.ui.gizmo.RDXSelectablePose3DGizmo; import us.ihmc.rdx.ui.graphics.RDXArmMultiBodyGraphic; import us.ihmc.rdx.ui.teleoperation.RDXIKSolverColors; -import us.ihmc.rdx.ui.widgets.ImGuiHandWidget; +import us.ihmc.rdx.ui.widgets.ImGuiArmIconWidget; import us.ihmc.robotModels.FullHumanoidRobotModel; import us.ihmc.robotics.EuclidCoreMissingTools; import us.ihmc.robotics.MultiBodySystemMissingTools; @@ -57,7 +57,7 @@ public class RDXHandPoseAction extends RDXActionNode handGraphicToControlFrameTransforms = new SideDependentList<>(); @@ -324,7 +324,7 @@ public void renderTreeViewIconArea() super.renderTreeViewIconArea(); boolean gizmoWasSelected = poseGizmo.getSelected().get(); - if (handIconWidget.render(definition.getSide(), ImGui.getFrameHeight(), gizmoWasSelected)) + if (armIconWidget.render(definition.getSide(), gizmoWasSelected)) { poseGizmo.setSelected(!gizmoWasSelected); } @@ -465,7 +465,7 @@ public void getRenderables(Array renderables, Pool pool) { if (state.getPalmFrame().isChildOfWorld()) { - if (!definition.getUsePredefinedJointAngles() && (getSelected() || poseGizmo.isSelected() || handIconWidget.getIsHovered())) + if (!definition.getUsePredefinedJointAngles() && (getSelected() || poseGizmo.isSelected() || armIconWidget.getIsHovered())) highlightModels.get(definition.getSide()).getRenderables(renderables, pool); poseGizmo.getVirtualRenderables(renderables, pool); diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiArmIconWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiArmIconWidget.java new file mode 100644 index 000000000000..33cbd7253530 --- /dev/null +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiArmIconWidget.java @@ -0,0 +1,71 @@ +package us.ihmc.rdx.ui.widgets; + +import imgui.ImGui; +import imgui.flag.ImGuiCol; +import imgui.flag.ImGuiMouseButton; +import us.ihmc.rdx.imgui.ImGuiTools; +import us.ihmc.robotics.robotSide.RobotSide; + +/** + * A clickable robot arm icon with hover state. + */ +public class ImGuiArmIconWidget +{ + private boolean isHovered; + private RobotSide side; + + public boolean render(RobotSide side, boolean isSelected) + { + this.side = side; + + float fontSize = ImGui.getFontSize(); + float scale = fontSize; + + float offsetX = ImGui.getCursorScreenPosX() + 0.0f * scale; + float offsetY = ImGui.getCursorScreenPosY() + 0.0f * scale + ImGui.getStyle().getFramePaddingY(); + + if (side == RobotSide.LEFT) + offsetX += scale; + else + offsetX += 0.9f * scale; + + float lineHeight = ImGui.getFrameHeight(); + float itemWidth = 1.8f * scale; + isHovered = ImGuiTools.isItemHovered(itemWidth, lineHeight); + + if (isHovered || isSelected) + { + int hoverColor = ImGui.getColorU32(ImGuiCol.ButtonHovered); + ImGui.getWindowDrawList().addCircleFilled(offsetX + flip(0.1f ) * scale, offsetY + 0.8f * scale, 0.21f * scale, hoverColor); + ImGui.getWindowDrawList().addCircleFilled(offsetX + flip(0.75f) * scale, offsetY + 0.5f * scale, 0.23f * scale, hoverColor); + ImGui.getWindowDrawList().addCircleFilled(offsetX + flip(0.3f ) * scale, offsetY + 0.1f * scale, 0.16f * scale, hoverColor); + } + + int lineColor = ImGui.getColorU32(ImGuiCol.Text); + ImGui.getWindowDrawList().addCircle(offsetX + flip(0.1f ) * scale, offsetY + 0.8f * scale, 0.21f * scale, lineColor); + ImGui.getWindowDrawList().addCircle(offsetX + flip(0.75f) * scale, offsetY + 0.5f * scale, 0.23f * scale, lineColor); + ImGui.getWindowDrawList().addCircle(offsetX + flip(0.3f ) * scale, offsetY + 0.1f * scale, 0.16f * scale, lineColor); + ImGui.getWindowDrawList().addLine(offsetX + flip(0.2f ) * scale, offsetY + .91f * scale, + offsetX + flip(0.68f) * scale, offsetY + 0.66f * scale, lineColor); + ImGui.getWindowDrawList().addLine(offsetX + flip(0.15f) * scale, offsetY + .6f * scale, + offsetX + flip(0.55f) * scale, offsetY + 0.45f * scale, lineColor); + ImGui.getWindowDrawList().addLine(offsetX + flip(0.55f) * scale, offsetY + .45f * scale, + offsetX + flip(0.27f) * scale, offsetY + 0.2f * scale, lineColor); + ImGui.getWindowDrawList().addLine(offsetX + flip(0.7f ) * scale, offsetY + .3f * scale, + offsetX + flip(0.4f ) * scale, offsetY + 0.1f * scale, lineColor); + + ImGui.setCursorPosX(ImGui.getCursorPosX() + itemWidth); + ImGui.newLine(); + return isHovered && ImGui.isMouseClicked(ImGuiMouseButton.Left); + } + + private float flip(float in) + { + return side == RobotSide.RIGHT ? in : -in; + } + + public boolean getIsHovered() + { + return isHovered; + } +} From e6c3e4036637f2ee39983ff7e8feb4f11608cf1a Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Thu, 16 Oct 2025 17:11:29 -0500 Subject: [PATCH 09/14] Failing on the goto icon. --- .../rdx/ui/behavior/logic/RDXGotoNode.java | 11 +++ .../rdx/ui/widgets/ImGuiGotoNodeWidget.java | 83 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiGotoNodeWidget.java diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXGotoNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXGotoNode.java index 7bba41600504..04ffe31642e8 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXGotoNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXGotoNode.java @@ -9,11 +9,13 @@ import us.ihmc.communication.crdt.CRDTInfo; import us.ihmc.rdx.imgui.ImGuiUniqueLabelMap; import us.ihmc.rdx.ui.behavior.sequence.RDXLeafNode; +import us.ihmc.rdx.ui.widgets.ImGuiGotoNodeWidget; import us.ihmc.tools.io.WorkspaceResourceDirectory; public class RDXGotoNode extends RDXLeafNode { private final ImGuiUniqueLabelMap labels = new ImGuiUniqueLabelMap(getClass()); + private final ImGuiGotoNodeWidget gotoNodeWidget = new ImGuiGotoNodeWidget(); public RDXGotoNode(long id, CRDTInfo crdtInfo, WorkspaceResourceDirectory saveFileDirectory) { @@ -32,6 +34,15 @@ public void renderContextMenuItems() super.renderContextMenuItems(); } + @Override + public void renderTreeViewIconArea() + { + super.renderTreeViewIconArea(); + + gotoNodeWidget.render(); + ImGui.sameLine(); + } + @Override protected void renderImGuiWidgetsInternal() { diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiGotoNodeWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiGotoNodeWidget.java new file mode 100644 index 000000000000..cdd311dc4dbf --- /dev/null +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiGotoNodeWidget.java @@ -0,0 +1,83 @@ +package us.ihmc.rdx.ui.widgets; + +import imgui.ImGui; +import imgui.ImVec2; +import imgui.flag.ImGuiCol; + +/** + * An icon for the goto node. + */ +public class ImGuiGotoNodeWidget +{ + private final ImVec2[] polygon = new ImVec2[15]; + + public ImGuiGotoNodeWidget() + { + for (int i = 0; i < polygon.length; i++) + polygon[i] = new ImVec2(); + } + + public void render() + { + float fontSize = ImGui.getFontSize(); + float scale = fontSize; + + float offsetX = ImGui.getCursorScreenPosX() + 0.0f * fontSize; + float offsetY = ImGui.getCursorScreenPosY() + 0.0f * fontSize + ImGui.getStyle().getFramePaddingY(); + + int lineColor = ImGui.getColorU32(ImGuiCol.Text); + +// polygon[0].set(0.75f, 0.18f); +// polygon[1].set(0.95f, 0.4f); +// polygon[2].set(0.77f, 0.4f); +// polygon[3].set(0.77f, 0.58f); +// polygon[4].set(0.95f, 0.78f); +// polygon[5].set(0.67f, 0.8f); +// polygon[6].set(0.35f, 0.76f); +// polygon[7].set(0.21f, 0.67f); +// polygon[8].set(0.33f, 0.59f); +// polygon[9].set(0.33f, 0.4f); +// polygon[10].set(0.05f, 0.4f); +// polygon[11].set(0.25f, 0.18f); +// polygon[12].set(0.33f, 0.59f); +// polygon[13].set(0.33f, 0.4f); +// polygon[14].set(0.05f, 0.4f); + + polygon[0].set(-2.687926f, 0.03162f); + polygon[1].set(-2.529811f, -0.158113f); + polygon[2].set(-1.6332f, -1.253726f); + polygon[3].set(-0.327405f, -2.035031f); + polygon[4].set(0.08561f, -1.348593f); + polygon[5].set(-1.603495f, -0.01118f); + polygon[6].set(0.0019f, -0.78514f); + polygon[7].set(2.84412f, -3.863391f); + polygon[8].set(2.838696f, 3.844866f); + polygon[9].set(-0.01118f, 0.827613f); + polygon[10].set(-1.650133f, -0.01853f); + polygon[11].set(0.08752f, 1.797066f); + polygon[12].set(1.264907f, 0.82219f); + polygon[13].set(3.333149f, 0.03354f); + polygon[14].set(-2.687926f, 0.03162f); + // polygon[1].set(-18.94445f * scale + offsetX, -1.527775f * scale + offsetY); +// polygon[2].set(-11.81481f * scale + offsetX, -13.03704f * scale + offsetY); +// polygon[3].set(0.2037f * scale + offsetX, -14.870369f * scale + offsetY); +// polygon[4].set(-13.546294f * scale + offsetX, 0.203703f * scale + offsetY); +// polygon[5].set(29.129634f * scale + offsetX, -34.527776f * scale + offsetY); +// polygon[6].set(18.84259f * scale + offsetX, 31.370371f * scale + offsetY); +// polygon[7].set(-17.62037f * scale + offsetX, 2.648146f * scale + offsetY); +// polygon[8].set(0.326f * scale + offsetX, 0.500f * scale + offsetY); +// polygon[9].set(-0.043f * scale + offsetX, 0.109f * scale + offsetY); +// polygon[10].set(-0.256f * scale + offsetX, -0.001f * scale + offsetY); +// polygon[11].set(-0.030f * scale + offsetX, 0.234f * scale + offsetY); +// polygon[12].set(0.122f * scale + offsetX, 0.108f * scale + offsetY); +// polygon[13].set(0.391f * scale + offsetX, 0.006f * scale + offsetY); +// polygon[14].set(-0.391f * scale + offsetX, 0.005f * scale + offsetY); + + for (int i = 0; i < polygon.length - 1; i++) + ImGui.getWindowDrawList().addLine(polygon[i].x * scale + offsetX, polygon[i].y * scale + offsetY, + polygon[i + 1].x * scale + offsetX, polygon[i + 1].y * scale + offsetY, lineColor); + + ImGui.setCursorPosX(ImGui.getCursorPosX() + scale); + ImGui.newLine(); + } +} From ea01564a504cff00653bb14945dea02798f37430 Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Fri, 17 Oct 2025 11:48:53 -0500 Subject: [PATCH 10/14] Fix goto node icon. --- .../src/libgdx/external/icons/goto.svg | 46 +++++++++++ .../rdx/ui/widgets/ImGuiGotoNodeWidget.java | 76 +++++-------------- .../ui/widgets/ImGuiSVGWidgetNormalizer.java | 50 +++++++----- 3 files changed, 99 insertions(+), 73 deletions(-) create mode 100644 ihmc-high-level-behaviors/src/libgdx/external/icons/goto.svg diff --git a/ihmc-high-level-behaviors/src/libgdx/external/icons/goto.svg b/ihmc-high-level-behaviors/src/libgdx/external/icons/goto.svg new file mode 100644 index 000000000000..b4a2c3aa899d --- /dev/null +++ b/ihmc-high-level-behaviors/src/libgdx/external/icons/goto.svg @@ -0,0 +1,46 @@ + + + + + + + + + + diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiGotoNodeWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiGotoNodeWidget.java index cdd311dc4dbf..64eb03831efb 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiGotoNodeWidget.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiGotoNodeWidget.java @@ -9,70 +9,34 @@ */ public class ImGuiGotoNodeWidget { - private final ImVec2[] polygon = new ImVec2[15]; - - public ImGuiGotoNodeWidget() + private final ImVec2[] polygon = new ImVec2[] { - for (int i = 0; i < polygon.length; i++) - polygon[i] = new ImVec2(); - } + new ImVec2(0.461f, 0.495f), + new ImVec2(0.461f, 0.28f), + new ImVec2(0.108f, 0.266f), + new ImVec2(-0.022f, 0.179f), + new ImVec2(-0.030f, -0.010f), + new ImVec2(0.140f, -0.029f), + new ImVec2(0.140f, -0.099f), + new ImVec2(-0.159f, -0.500f), + new ImVec2(-0.461f, -0.095f), + new ImVec2(-0.459f, -0.015f), + new ImVec2(-0.292f, -0.010f), + new ImVec2(-0.301f, 0.139f), + new ImVec2(-0.263f, 0.351f), + new ImVec2(-0.093f, 0.482f), + new ImVec2(0.177f, 0.500f), + new ImVec2(0.461f, 0.495f), + }; public void render() { float fontSize = ImGui.getFontSize(); float scale = fontSize; - - float offsetX = ImGui.getCursorScreenPosX() + 0.0f * fontSize; - float offsetY = ImGui.getCursorScreenPosY() + 0.0f * fontSize + ImGui.getStyle().getFramePaddingY(); - + float offsetX = ImGui.getCursorScreenPosX() + 0.5f * fontSize; + float offsetY = ImGui.getCursorScreenPosY() + 0.4f * fontSize + ImGui.getStyle().getFramePaddingY(); int lineColor = ImGui.getColorU32(ImGuiCol.Text); -// polygon[0].set(0.75f, 0.18f); -// polygon[1].set(0.95f, 0.4f); -// polygon[2].set(0.77f, 0.4f); -// polygon[3].set(0.77f, 0.58f); -// polygon[4].set(0.95f, 0.78f); -// polygon[5].set(0.67f, 0.8f); -// polygon[6].set(0.35f, 0.76f); -// polygon[7].set(0.21f, 0.67f); -// polygon[8].set(0.33f, 0.59f); -// polygon[9].set(0.33f, 0.4f); -// polygon[10].set(0.05f, 0.4f); -// polygon[11].set(0.25f, 0.18f); -// polygon[12].set(0.33f, 0.59f); -// polygon[13].set(0.33f, 0.4f); -// polygon[14].set(0.05f, 0.4f); - - polygon[0].set(-2.687926f, 0.03162f); - polygon[1].set(-2.529811f, -0.158113f); - polygon[2].set(-1.6332f, -1.253726f); - polygon[3].set(-0.327405f, -2.035031f); - polygon[4].set(0.08561f, -1.348593f); - polygon[5].set(-1.603495f, -0.01118f); - polygon[6].set(0.0019f, -0.78514f); - polygon[7].set(2.84412f, -3.863391f); - polygon[8].set(2.838696f, 3.844866f); - polygon[9].set(-0.01118f, 0.827613f); - polygon[10].set(-1.650133f, -0.01853f); - polygon[11].set(0.08752f, 1.797066f); - polygon[12].set(1.264907f, 0.82219f); - polygon[13].set(3.333149f, 0.03354f); - polygon[14].set(-2.687926f, 0.03162f); - // polygon[1].set(-18.94445f * scale + offsetX, -1.527775f * scale + offsetY); -// polygon[2].set(-11.81481f * scale + offsetX, -13.03704f * scale + offsetY); -// polygon[3].set(0.2037f * scale + offsetX, -14.870369f * scale + offsetY); -// polygon[4].set(-13.546294f * scale + offsetX, 0.203703f * scale + offsetY); -// polygon[5].set(29.129634f * scale + offsetX, -34.527776f * scale + offsetY); -// polygon[6].set(18.84259f * scale + offsetX, 31.370371f * scale + offsetY); -// polygon[7].set(-17.62037f * scale + offsetX, 2.648146f * scale + offsetY); -// polygon[8].set(0.326f * scale + offsetX, 0.500f * scale + offsetY); -// polygon[9].set(-0.043f * scale + offsetX, 0.109f * scale + offsetY); -// polygon[10].set(-0.256f * scale + offsetX, -0.001f * scale + offsetY); -// polygon[11].set(-0.030f * scale + offsetX, 0.234f * scale + offsetY); -// polygon[12].set(0.122f * scale + offsetX, 0.108f * scale + offsetY); -// polygon[13].set(0.391f * scale + offsetX, 0.006f * scale + offsetY); -// polygon[14].set(-0.391f * scale + offsetX, 0.005f * scale + offsetY); - for (int i = 0; i < polygon.length - 1; i++) ImGui.getWindowDrawList().addLine(polygon[i].x * scale + offsetX, polygon[i].y * scale + offsetY, polygon[i + 1].x * scale + offsetX, polygon[i + 1].y * scale + offsetY, lineColor); diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSVGWidgetNormalizer.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSVGWidgetNormalizer.java index ad4e2bcfbd37..f7d6333a9823 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSVGWidgetNormalizer.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSVGWidgetNormalizer.java @@ -12,15 +12,22 @@ /** * Utility to convert SVG vertices to a format pastable into * custom ImGui widget classes. + * + * In Inksscape, set SVG Ouput in preferences to Path string format: "Absolute" + * + * FIXME: This class is still not feature complete. */ public class ImGuiSVGWidgetNormalizer { public ImGuiSVGWidgetNormalizer() { // Paste SVG path n here - String pathDString = - """ - M 75.186018,102.25503 73.21275,110.75519 80.650436,116.37138 90.820271,114.09455 95.070368,118.79999 101.29367,113.18382 96.436443,109.3891 100.99011,100.88892 94.463176,92.540541 82.471889,93.147694 83.5344,96.942414 93.248855,98.612091 92.338159,107.11227 80.195059,110.29983 79.132551,101.79966 Z + String pathDString = """ + M 99.303402,65.896674 + V 63.783598 + L 95.971674,63.727697 94.741843,62.911534 94.663579,61.122686 + H 96.262363 + L 96.273542,60.284163 93.444927,56.494044 90.593951,60.317705 90.616311,61.077966 92.192733,61.122686 92.103291,62.531405 92.46106,64.532679 94.071023,65.773691 96.620133,65.941394 Z """; String[] commands = pathDString.split("\\s+"); @@ -35,15 +42,17 @@ public ImGuiSVGWidgetNormalizer() { command = commands[i]; - if (command.equalsIgnoreCase("m")) // Move to + if (command.equals("m")) // Move to { String[] coordinates = commands[++i].split(","); - drawFrame = ReferenceFrameTools.constructFrameWithUnchangingTransformToParent("drawFrame", ReferenceFrame.getWorldFrame(), - new RigidBodyTransform(new RotationMatrix(), new Point3D(Double.parseDouble(coordinates[0]), - Double.parseDouble(coordinates[1]), - 0.0))); + drawFrame = ReferenceFrameTools.constructFrameWithUnchangingTransformToParent("drawFrame", + ReferenceFrame.getWorldFrame(), + new RigidBodyTransform(new RotationMatrix(), + new Point3D(Double.parseDouble(coordinates[0]), + Double.parseDouble(coordinates[1]), + 0.0))); } - else if (command.equals("Z")) // Close path + else if (command.equalsIgnoreCase("z")) // Close path { vertices.add(new FramePoint2D(vertices.get(0))); } @@ -51,12 +60,17 @@ else if (command.equals("Z")) // Close path continue; } - if (command.equalsIgnoreCase("m")) // A vertex + if (command.equals("M")) // A vertex { String[] coordinates = commands[i].split(","); ReferenceFrame vertexFrame = ReferenceFrame.getWorldFrame(); // When using absolute mode this is world apparently vertices.add(new FramePoint2D(vertexFrame, Double.parseDouble(coordinates[0]), Double.parseDouble(coordinates[1]))); } + else if (command.equals("m")) // relative mode TODO fix + { + String[] coordinates = commands[i].split(","); + vertices.add(new FramePoint2D(drawFrame, Double.parseDouble(coordinates[0]), Double.parseDouble(coordinates[1]))); + } else if (command.equals("L")) // Line to { String[] coordinates = commands[i].split(","); @@ -66,14 +80,14 @@ else if (command.equals("L")) // Line to else if (command.equals("H")) // Horizontal line to { FramePoint2D lastPoint = vertices.get(vertices.size() - 1); -// lastPoint.changeFrame(ReferenceFrame.getWorldFrame()); + // lastPoint.changeFrame(ReferenceFrame.getWorldFrame()); FramePoint2D newPoint = new FramePoint2D(drawFrame, lastPoint.getX32() + Double.parseDouble(commands[i]), lastPoint.getY32()); vertices.add(newPoint); } else if (command.equals("V")) // Vertical line to { FramePoint2D lastPoint = vertices.get(vertices.size() - 1); -// lastPoint.changeFrame(ReferenceFrame.getWorldFrame()); + // lastPoint.changeFrame(ReferenceFrame.getWorldFrame()); FramePoint2D newPoint = new FramePoint2D(drawFrame, lastPoint.getX32(), lastPoint.getY32() + Double.parseDouble(commands[i])); vertices.add(newPoint); } @@ -101,10 +115,12 @@ else if (command.equals("V")) // Vertical line to double maxDimension = Math.max(width, height); - - ReferenceFrame centerFrame = ReferenceFrameTools.constructFrameWithUnchangingTransformToParent("centerFrame", ReferenceFrame.getWorldFrame(), - new RigidBodyTransform(new RotationMatrix(), - new Point3D(xMin + width / 2.0, yMin + height / 2.0, 0.0))); + ReferenceFrame centerFrame = ReferenceFrameTools.constructFrameWithUnchangingTransformToParent("centerFrame", + ReferenceFrame.getWorldFrame(), + new RigidBodyTransform(new RotationMatrix(), + new Point3D(xMin + width / 2.0, + yMin + height / 2.0, + 0.0))); for (FramePoint2D vertex : vertices) { @@ -115,7 +131,7 @@ else if (command.equals("V")) // Vertical line to System.out.println("private final ArrayList vertices = new ArrayList<>();\n{"); for (FramePoint2D vertex : vertices) { - System.out.printf(" vertices.add(new Point2D32(%.3ff, %.3ff));%n", vertex.getX(), vertex.getY()); + System.out.printf(" new ImVec2(%.3ff, %.3ff),%n", vertex.getX(), vertex.getY()); } System.out.println("}"); } From 24a712e7d0379f8606581f97decd4bbd96b0c6da Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Fri, 17 Oct 2025 14:20:31 -0500 Subject: [PATCH 11/14] Checkpoint icon. --- .../src/libgdx/external/icons/checkpoint.svg | 47 +++++++++++++++++++ .../behavior/actions/RDXCheckPointNode.java | 14 +++++- .../ui/widgets/ImGuiCheckpointNodeWidget.java | 47 +++++++++++++++++++ 3 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 ihmc-high-level-behaviors/src/libgdx/external/icons/checkpoint.svg create mode 100644 ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiCheckpointNodeWidget.java diff --git a/ihmc-high-level-behaviors/src/libgdx/external/icons/checkpoint.svg b/ihmc-high-level-behaviors/src/libgdx/external/icons/checkpoint.svg new file mode 100644 index 000000000000..a2b845d95591 --- /dev/null +++ b/ihmc-high-level-behaviors/src/libgdx/external/icons/checkpoint.svg @@ -0,0 +1,47 @@ + + + + + + + + + + diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXCheckPointNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXCheckPointNode.java index ced032ac8a64..19b0e8f8eca7 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXCheckPointNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXCheckPointNode.java @@ -1,21 +1,31 @@ package us.ihmc.rdx.ui.behavior.actions; +import imgui.ImGui; import us.ihmc.behaviors.sequence.actions.CheckPointNodeDefinition; import us.ihmc.behaviors.sequence.actions.CheckPointNodeState; import us.ihmc.communication.crdt.CRDTInfo; -import us.ihmc.rdx.imgui.ImGuiUniqueLabelMap; import us.ihmc.rdx.ui.behavior.sequence.RDXLeafNode; +import us.ihmc.rdx.ui.widgets.ImGuiCheckpointNodeWidget; import us.ihmc.tools.io.WorkspaceResourceDirectory; public class RDXCheckPointNode extends RDXLeafNode { - private final ImGuiUniqueLabelMap labels = new ImGuiUniqueLabelMap(getClass()); + private final ImGuiCheckpointNodeWidget checkpointNodeWidget = new ImGuiCheckpointNodeWidget(); public RDXCheckPointNode(long id, CRDTInfo crdtInfo, WorkspaceResourceDirectory saveFileDirectory) { super(new CheckPointNodeState(id, crdtInfo, saveFileDirectory)); } + @Override + public void renderTreeViewIconArea() + { + super.renderTreeViewIconArea(); + + checkpointNodeWidget.render(); + ImGui.sameLine(); + } + @Override public String getLeafTypeTitle() { diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiCheckpointNodeWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiCheckpointNodeWidget.java new file mode 100644 index 000000000000..342539dfe3a2 --- /dev/null +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiCheckpointNodeWidget.java @@ -0,0 +1,47 @@ +package us.ihmc.rdx.ui.widgets; + +import imgui.ImGui; +import imgui.ImVec2; +import imgui.flag.ImGuiCol; + +/** + * An icon for the goto node. + */ +public class ImGuiCheckpointNodeWidget +{ + private final ImVec2[] polygon = new ImVec2[] + { + new ImVec2(-0.316f, 0.500f), + new ImVec2(-0.315f, -0.359f), + new ImVec2(-0.352f, -0.375f), + new ImVec2(-0.364f, -0.436f), + new ImVec2(-0.328f, -0.499f), + new ImVec2(-0.240f, -0.500f), + new ImVec2(-0.191f, -0.459f), + new ImVec2(-0.191f, -0.389f), + new ImVec2(-0.223f, -0.355f), + new ImVec2(-0.307f, -0.355f), + new ImVec2(-0.227f, -0.353f), + new ImVec2(0.364f, -0.073f), + new ImVec2(-0.223f, 0.096f), + new ImVec2(-0.228f, -0.352f), + new ImVec2(-0.227f, 0.500f), + new ImVec2(-0.316f, 0.500f), + }; + + public void render() + { + float fontSize = ImGui.getFontSize(); + float scale = fontSize; + float offsetX = ImGui.getCursorScreenPosX() + 0.5f * fontSize; + float offsetY = ImGui.getCursorScreenPosY() + 0.4f * fontSize + ImGui.getStyle().getFramePaddingY(); + int lineColor = ImGui.getColorU32(ImGuiCol.Text); + + for (int i = 0; i < polygon.length - 1; i++) + ImGui.getWindowDrawList().addLine(polygon[i].x * scale + offsetX, polygon[i].y * scale + offsetY, + polygon[i + 1].x * scale + offsetX, polygon[i + 1].y * scale + offsetY, lineColor); + + ImGui.setCursorPosX(ImGui.getCursorPosX() + scale); + ImGui.newLine(); + } +} From 482cf17e32e9fa20d25bd909e3a677b9f873e50f Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Fri, 17 Oct 2025 14:44:48 -0500 Subject: [PATCH 12/14] Highlight collapse arrows on hover. --- .../rdx/ui/behavior/sequence/RDXActionSequence.java | 2 +- .../rdx/ui/behavior/sequence/RDXFallbackNode.java | 2 +- .../rdx/ui/behavior/tree/RDXBehaviorTreeNode.java | 11 ----------- .../ui/behavior/tree/RDXBehaviorTreeRootNode.java | 2 +- .../us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java | 13 ++++++++++++- .../us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java | 13 ++++++++++++- .../rdx/ui/widgets/ImGuiSequenceIconWidget.java | 13 ++++++++++++- 7 files changed, 39 insertions(+), 17 deletions(-) diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXActionSequence.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXActionSequence.java index 4430adb601a3..fd8a0818bcfd 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXActionSequence.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXActionSequence.java @@ -30,7 +30,7 @@ public void renderTreeViewIconArea() { super.renderTreeViewIconArea(); - if (sequenceIconWidget.render()) + if (sequenceIconWidget.render(!getChildren().isEmpty() && !getTreeWidgetExpanded())) { setSpecificWidgetOnRowClicked(); setTreeWidgetExpanded(!getTreeWidgetExpanded()); diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java index a53ebd4c5c2b..6beb30cc2597 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java @@ -31,7 +31,7 @@ public void renderTreeViewIconArea() super.renderTreeViewIconArea(); ImGui.setCursorPosX(ImGui.getCursorPosX() + ImGui.getStyle().getItemSpacingX()); - if (fallbackWidget.render()) + if (fallbackWidget.render(!getChildren().isEmpty() && !getTreeWidgetExpanded())) { setSpecificWidgetOnRowClicked(); setTreeWidgetExpanded(!getTreeWidgetExpanded()); diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java index 45cee4ed58db..53d8a847da66 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java @@ -130,17 +130,6 @@ public void renderGeneralRowBeginWidgets() { ImGui.getWindowDrawList().addRectFilled(lineMin.x, lineMin.y, lineMax.x, lineMax.y, ImGui.getColorU32(ImGuiCol.MenuBarBg)); } - - if (!getChildren().isEmpty() && !treeWidgetExpanded) - { - float offsetX = ImGui.getCursorScreenPosX(); - float offsetY = ImGui.getCursorScreenPosY() + ImGui.getFrameHeight() * 0.2f; - float width = ImGui.getFontSize() / 2.5f; - float height = ImGui.getFrameHeight() * 0.5f; - int color = ImGui.getColorU32(ImGuiCol.Text); - ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY + height, color); - ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY, color); - } } public void renderTreeViewIconArea() diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeRootNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeRootNode.java index e45334158ed2..aadbf188ed1b 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeRootNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeRootNode.java @@ -89,7 +89,7 @@ public void renderTreeViewIconArea() { super.renderTreeViewIconArea(); - if (rootIconWidget.render()) + if (rootIconWidget.render(!getChildren().isEmpty() && !getTreeWidgetExpanded())) { setSpecificWidgetOnRowClicked(); setTreeWidgetExpanded(!getTreeWidgetExpanded()); diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java index a730e1431cf6..9fcd29e0f7ee 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java @@ -57,7 +57,7 @@ public ImGuiFallbackWidget() bottomPolygon[i] = new ImVec2(); } - public boolean render() + public boolean render(boolean renderCollapsedArrow) { float lineHeight = ImGui.getFrameHeight(); float fontSize = ImGui.getFontSize(); @@ -144,6 +144,17 @@ public boolean render() for (int i = 0; i < bottomPolygon.length - 1; i++) ImGui.getWindowDrawList().addLine(bottomPolygon[i].x, bottomPolygon[i].y, bottomPolygon[i + 1].x, bottomPolygon[i + 1].y, lineColor); + if (renderCollapsedArrow) // collapsed arrow + { + float offsetX = ImGui.getCursorScreenPosX() - scale * 0.6f; + float offsetY = ImGui.getCursorScreenPosY() + ImGui.getFrameHeight() * 0.2f; + float width = ImGui.getFontSize() / 2.5f; + float height = ImGui.getFrameHeight() * 0.5f; + int color = isHovered ? ImGui.getColorU32(ImGuiCol.ButtonHovered) : ImGui.getColorU32(ImGuiCol.Text); + ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY + height, color); + ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY, color); + } + ImGui.setCursorPosX(ImGui.getCursorPosX() + (itemWidth * 0.8f)); ImGui.newLine(); diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java index bfaa78e51b27..02e9fbd6b605 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java @@ -10,7 +10,7 @@ */ public class ImGuiRootIconWidget { - public boolean render() + public boolean render(boolean renderCollapsedArrow) { float lineHeight = ImGui.getFrameHeight(); float fontSize = ImGui.getFontSize(); @@ -49,6 +49,17 @@ public boolean render() ImGui.getWindowDrawList().addCircle((-wide * scale) + offsetX, (down * scale) + offsetY, circleSize * scale, lineColor); ImGui.getWindowDrawList().addCircle((wide * scale) + offsetX, (down * scale) + offsetY, circleSize * scale, lineColor); + if (renderCollapsedArrow) // collapsed arrow + { + offsetX = ImGui.getCursorScreenPosX(); + offsetY = ImGui.getCursorScreenPosY() + ImGui.getFrameHeight() * 0.2f; + float width = ImGui.getFontSize() / 2.5f; + float height = ImGui.getFrameHeight() * 0.5f; + int color = isHovered ? ImGui.getColorU32(ImGuiCol.ButtonHovered) : ImGui.getColorU32(ImGuiCol.Text); + ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY + height, color); + ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY, color); + } + ImGui.setCursorPosX(ImGui.getCursorPosX() + itemWidth); ImGui.newLine(); return isHovered && ImGui.isMouseClicked(ImGuiMouseButton.Left); diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSequenceIconWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSequenceIconWidget.java index 98180d79258d..20f1fa4ba3d1 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSequenceIconWidget.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSequenceIconWidget.java @@ -10,7 +10,7 @@ */ public class ImGuiSequenceIconWidget { - public boolean render() + public boolean render(boolean renderCollapsedArrow) { float lineHeight = ImGui.getFrameHeight(); float fontSize = ImGui.getFontSize(); @@ -144,6 +144,17 @@ public boolean render() offsetY + scale * baseBottomRightY, lineColor); + if (renderCollapsedArrow) // collapsed arrow + { + offsetX = ImGui.getCursorScreenPosX(); + offsetY = ImGui.getCursorScreenPosY() + ImGui.getFrameHeight() * 0.2f; + float width = ImGui.getFontSize() / 2.5f; + float height = ImGui.getFrameHeight() * 0.5f; + int color = isHovered ? ImGui.getColorU32(ImGuiCol.ButtonHovered) : ImGui.getColorU32(ImGuiCol.Text); + ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY + height, color); + ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY, color); + } + ImGui.setCursorPosX(ImGui.getCursorPosX() + itemWidth); ImGui.newLine(); return isHovered && ImGui.isMouseClicked(ImGuiMouseButton.Left); From efd0514b7c6e4bafe37075ddd06087e0354ba451 Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Fri, 17 Oct 2025 15:11:30 -0500 Subject: [PATCH 13/14] Pull spacing details into fallback widget. --- .../us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java | 2 -- .../java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java | 7 +++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java index 6beb30cc2597..8945347e1e12 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java @@ -30,14 +30,12 @@ public void renderTreeViewIconArea() { super.renderTreeViewIconArea(); - ImGui.setCursorPosX(ImGui.getCursorPosX() + ImGui.getStyle().getItemSpacingX()); if (fallbackWidget.render(!getChildren().isEmpty() && !getTreeWidgetExpanded())) { setSpecificWidgetOnRowClicked(); setTreeWidgetExpanded(!getTreeWidgetExpanded()); } ImGui.sameLine(); - ImGui.setCursorPosX(ImGui.getCursorPosX() + ImGui.getStyle().getItemSpacingX()); } @Override diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java index 9fcd29e0f7ee..f8c41f9fde55 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java @@ -87,9 +87,12 @@ public boolean render(boolean renderCollapsedArrow) xMax = Math.max(xMax, bottomPolygon[i].x); } - float itemWidth = xMax - xMin; + float spacing = 1.5f * ImGui.getStyle().getItemSpacingX(); + float itemWidth = xMax - xMin + spacing + scale * 0.3f; boolean isHovered = ImGuiTools.isItemHovered(itemWidth, lineHeight); + ImGui.setCursorPosX(ImGui.getCursorPosX() + spacing); + float cursorScreenPosX = ImGui.getCursorScreenPosX(); float cursorScreenPosY = ImGui.getCursorScreenPosY(); for (int i = 0; i < topPolygon.length; i++) @@ -155,7 +158,7 @@ public boolean render(boolean renderCollapsedArrow) ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY, color); } - ImGui.setCursorPosX(ImGui.getCursorPosX() + (itemWidth * 0.8f)); + ImGui.setCursorPosX(ImGui.getCursorPosX() + (itemWidth * 0.8f) + 0.1f * ImGui.getStyle().getItemSpacingX()); ImGui.newLine(); return isHovered && ImGui.isMouseClicked(ImGuiMouseButton.Left); From b2120d6891ef18d24cb33fa904d219ceec60047a Mon Sep 17 00:00:00 2001 From: Duncan Calvert Date: Fri, 17 Oct 2025 17:00:00 -0500 Subject: [PATCH 14/14] Put extra icons to the right side. Fix expand collapse UI. --- .../behavior/actions/RDXCheckPointNode.java | 7 +- .../actions/RDXFootstepPlanAction.java | 7 +- .../behavior/actions/RDXHandPoseAction.java | 7 +- .../actions/RDXSakeHandCommandAction.java | 7 +- .../ui/behavior/logic/RDXConditionNode.java | 7 +- .../rdx/ui/behavior/logic/RDXGotoNode.java | 7 +- .../behavior/sequence/RDXActionSequence.java | 14 ++-- .../ui/behavior/sequence/RDXFallbackNode.java | 14 ++-- .../rdx/ui/behavior/sequence/RDXLeafNode.java | 32 +++---- .../ui/behavior/tree/RDXBehaviorTreeNode.java | 84 +++++++++++-------- .../tree/RDXBehaviorTreeRootNode.java | 12 ++- .../RDXBehaviorTreeWidgetsVerticalLayout.java | 9 +- .../rdx/ui/widgets/ImGuiFallbackWidget.java | 13 +-- .../rdx/ui/widgets/ImGuiRootIconWidget.java | 13 +-- .../ui/widgets/ImGuiSequenceIconWidget.java | 13 +-- 15 files changed, 113 insertions(+), 133 deletions(-) diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXCheckPointNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXCheckPointNode.java index 19b0e8f8eca7..66a5b3fb6d95 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXCheckPointNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXCheckPointNode.java @@ -18,12 +18,13 @@ public RDXCheckPointNode(long id, CRDTInfo crdtInfo, WorkspaceResourceDirectory } @Override - public void renderTreeViewIconArea() + public void renderTreeViewRow() { - super.renderTreeViewIconArea(); + super.renderRowBeginning(); + super.renderEditableName(); - checkpointNodeWidget.render(); ImGui.sameLine(); + checkpointNodeWidget.render(); } @Override diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXFootstepPlanAction.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXFootstepPlanAction.java index 58ecfb531510..c62e6b2a7c1a 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXFootstepPlanAction.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXFootstepPlanAction.java @@ -278,12 +278,13 @@ public void update() } @Override - public void renderTreeViewIconArea() + public void renderTreeViewRow() { - super.renderTreeViewIconArea(); + super.renderRowBeginning(); + super.renderEditableName(); - footstepsWidget.render(ImGui.getFrameHeight()); ImGui.sameLine(); + footstepsWidget.render(ImGui.getFrameHeight()); } @Override diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXHandPoseAction.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXHandPoseAction.java index 5a7839b119e5..a0bea7f96d6a 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXHandPoseAction.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXHandPoseAction.java @@ -319,17 +319,18 @@ private void visualizeIK() } @Override - public void renderTreeViewIconArea() + public void renderTreeViewRow() { - super.renderTreeViewIconArea(); + super.renderRowBeginning(); + super.renderEditableName(); + ImGui.sameLine(); boolean gizmoWasSelected = poseGizmo.getSelected().get(); if (armIconWidget.render(definition.getSide(), gizmoWasSelected)) { poseGizmo.setSelected(!gizmoWasSelected); } - ImGui.sameLine(); } @Override diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXSakeHandCommandAction.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXSakeHandCommandAction.java index e1b22ff57f7e..c7b6686f0be0 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXSakeHandCommandAction.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/actions/RDXSakeHandCommandAction.java @@ -83,12 +83,13 @@ protected void renderImGuiWidgetsInternal() } @Override - public void renderTreeViewIconArea() + public void renderTreeViewRow() { - super.renderTreeViewIconArea(); + super.renderRowBeginning(); + super.renderEditableName(); - gripperWidget.render(definition.getSide(), ImGui.getFrameHeight()); ImGui.sameLine(); + gripperWidget.render(definition.getSide(), ImGui.getFrameHeight()); } @Override diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXConditionNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXConditionNode.java index 1a398caac9bb..8f200c6c44dc 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXConditionNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXConditionNode.java @@ -33,12 +33,13 @@ public RDXConditionNode(long id, CRDTInfo crdtInfo, WorkspaceResourceDirectory s } @Override - public void renderTreeViewIconArea() + public void renderTreeViewRow() { - super.renderTreeViewIconArea(); + super.renderRowBeginning(); + super.renderEditableName(); - conditionIconWidget.render(); ImGui.sameLine(); + conditionIconWidget.render(); } @Override diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXGotoNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXGotoNode.java index 04ffe31642e8..b77f968a0766 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXGotoNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/logic/RDXGotoNode.java @@ -35,12 +35,13 @@ public void renderContextMenuItems() } @Override - public void renderTreeViewIconArea() + public void renderTreeViewRow() { - super.renderTreeViewIconArea(); + super.renderRowBeginning(); + super.renderEditableName(); - gotoNodeWidget.render(); ImGui.sameLine(); + gotoNodeWidget.render(); } @Override diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXActionSequence.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXActionSequence.java index fd8a0818bcfd..0c8ff0fea087 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXActionSequence.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXActionSequence.java @@ -4,14 +4,12 @@ import us.ihmc.behaviors.sequence.ActionSequenceDefinition; import us.ihmc.behaviors.sequence.ActionSequenceState; import us.ihmc.communication.crdt.CRDTInfo; -import us.ihmc.rdx.imgui.ImGuiUniqueLabelMap; import us.ihmc.rdx.ui.behavior.tree.RDXBehaviorTreeNode; import us.ihmc.rdx.ui.widgets.ImGuiSequenceIconWidget; import us.ihmc.tools.io.WorkspaceResourceDirectory; public class RDXActionSequence extends RDXBehaviorTreeNode { - private final ImGuiUniqueLabelMap labels = new ImGuiUniqueLabelMap(getClass()); private final ImGuiSequenceIconWidget sequenceIconWidget = new ImGuiSequenceIconWidget(); public RDXActionSequence(long id, CRDTInfo crdtInfo, WorkspaceResourceDirectory saveFileDirectory) @@ -26,16 +24,14 @@ public void update() } @Override - public void renderTreeViewIconArea() + public void renderTreeViewRow() { - super.renderTreeViewIconArea(); + super.renderRowBeginning(); + + sequenceIconWidget.render(); - if (sequenceIconWidget.render(!getChildren().isEmpty() && !getTreeWidgetExpanded())) - { - setSpecificWidgetOnRowClicked(); - setTreeWidgetExpanded(!getTreeWidgetExpanded()); - } ImGui.sameLine(); + super.renderEditableName(); } @Override diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java index 8945347e1e12..bbeeaced03cc 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXFallbackNode.java @@ -4,14 +4,12 @@ import us.ihmc.behaviors.sequence.FallbackNodeDefinition; import us.ihmc.behaviors.sequence.FallbackNodeState; import us.ihmc.communication.crdt.CRDTInfo; -import us.ihmc.rdx.imgui.ImGuiUniqueLabelMap; import us.ihmc.rdx.ui.behavior.tree.RDXBehaviorTreeNode; import us.ihmc.rdx.ui.widgets.ImGuiFallbackWidget; import us.ihmc.tools.io.WorkspaceResourceDirectory; public class RDXFallbackNode extends RDXBehaviorTreeNode { - private final ImGuiUniqueLabelMap labels = new ImGuiUniqueLabelMap(getClass()); private final ImGuiFallbackWidget fallbackWidget = new ImGuiFallbackWidget(); public RDXFallbackNode(long id, CRDTInfo crdtInfo, WorkspaceResourceDirectory saveFileDirectory) @@ -26,16 +24,14 @@ public void update() } @Override - public void renderTreeViewIconArea() + public void renderTreeViewRow() { - super.renderTreeViewIconArea(); + super.renderRowBeginning(); + + fallbackWidget.render(); - if (fallbackWidget.render(!getChildren().isEmpty() && !getTreeWidgetExpanded())) - { - setSpecificWidgetOnRowClicked(); - setTreeWidgetExpanded(!getTreeWidgetExpanded()); - } ImGui.sameLine(); + super.renderEditableName(); } @Override diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java index 21a955d54f8a..3df54c99bc1e 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/sequence/RDXLeafNode.java @@ -6,7 +6,6 @@ import us.ihmc.behaviors.behaviorTree.BehaviorTreeTools; import us.ihmc.behaviors.sequence.LeafNodeDefinition; import us.ihmc.behaviors.sequence.LeafNodeState; -import us.ihmc.rdx.imgui.ImGuiExpandCollapseRenderer; import us.ihmc.rdx.imgui.ImGuiFlashingColors; import us.ihmc.rdx.imgui.ImGuiFlashingText; import us.ihmc.rdx.imgui.ImGuiTools; @@ -49,36 +48,41 @@ public void update() } @Override - public void renderTreeViewIconArea() + public void renderRowBeginning() { + super.renderRowBeginning(); + RDXBehaviorTreeRootNode actionSequence = RDXBehaviorTreeTools.findRootNode(this); if (actionSequence != null) { // Give the arrow a little space to the left, like the other icons ImGui.setCursorPosX(ImGui.getCursorPosX() + ImGui.getStyle().getItemSpacingX()); - // Not displaying this now until we calculate it correctly. @dcalvert - if (state.getConcurrencyRank() != 1) - { - ImGui.pushStyleColor(ImGuiCol.Text, ImGui.getColorU32(ImGuiCol.TextDisabled)); - String text = state.getConcurrencyRank() == 1 ? " " : String.valueOf(state.getConcurrencyRank()); - ImGui.setCursorPosX(ImGui.getCursorPosX() - ImGuiTools.calcTextSizeX(text) - ImGui.getStyle().getItemSpacingX()); - ImGui.text(text); - ImGui.popStyleColor(); - ImGui.sameLine(); - } - boolean colorArrow = state.getIsNextForExecution() || state.getIsExecuting(); int arrowColor = state.getIsNextForExecution() ? ImGuiTools.GREEN : isExecutingFlashingColor.getColor(state.getIsExecuting()); if (hollowArrowRenderer.render(colorArrow, arrowColor, ImGui.getFrameHeight())) { - setSpecificWidgetOnRowClicked(); + anySpecificWidgetOnLineClicked = true; actionSequence.getState().setExecutionNextIndex(state.getLeafIndex()); } ImGui.sameLine(); } } + public void renderConcurrencyRank() + { + // Probably better to display some parallel bars like a Git log view maybe. @dcalvert + if (state.getConcurrencyRank() != 1) + { + ImGui.pushStyleColor(ImGuiCol.Text, ImGui.getColorU32(ImGuiCol.TextDisabled)); + String text = state.getConcurrencyRank() == 1 ? " " : String.valueOf(state.getConcurrencyRank()); + ImGui.setCursorPosX(ImGui.getCursorPosX() - ImGuiTools.calcTextSizeX(text) - ImGui.getStyle().getItemSpacingX()); + ImGui.text(text); + ImGui.popStyleColor(); + ImGui.sameLine(); + } + } + @Override public void renderNodeSettingsWidgets() { diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java index 53d8a847da66..9d972ea1f9d4 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeNode.java @@ -14,7 +14,6 @@ import us.ihmc.communication.crdt.CRDTInfo; import us.ihmc.rdx.imgui.ImGuiTools; import us.ihmc.rdx.imgui.ImGuiUniqueLabelMap; -import us.ihmc.rdx.imgui.ImGuiVerticalAligner; import us.ihmc.rdx.input.ImGui3DViewInput; import us.ihmc.rdx.ui.RDXBaseUI; import us.ihmc.rdx.ui.tools.ImGuiScrollableLogArea; @@ -46,17 +45,17 @@ public class RDXBehaviorTreeNode, private final ImGuiUniqueLabelMap labels = new ImGuiUniqueLabelMap(getClass()); private final ImBoolean selected = new ImBoolean(); private transient final ImVec2 lineMin = new ImVec2(); + private transient final ImVec2 indentMin = new ImVec2(); private transient final ImVec2 lineMax = new ImVec2(); private boolean mouseHoveringNodeLine; - private boolean anySpecificWidgetOnLineClicked = false; - private boolean treeWidgetExpanded = false; + protected boolean anySpecificWidgetOnLineClicked = false; + protected boolean treeWidgetExpanded = false; private int previousNumberOfChildren = 0; private boolean isNameBeingEdited = false; private transient final ImString imNodeNameText = new ImString(); private transient final ImString notesText = new ImString(1500); private final String nodePopupID = labels.get("Node popup"); private String modalPopupID = labels.get("Create node"); - private final ImGuiVerticalAligner childrenDescriptionAligner = new ImGuiVerticalAligner(); private final ImGuiScrollableLogArea logArea = new ImGuiScrollableLogArea(); /** For extending types. */ @@ -113,47 +112,81 @@ public void process3DViewInput(ImGui3DViewInput input) } - public void renderGeneralRowBeginWidgets() + /** Override to add node specific stuff */ + public void renderTreeViewRow() + { + renderRowBeginning(); + renderEditableName(); + } + + public void renderRowBeginning() { anySpecificWidgetOnLineClicked = false; ImGui.dummy(0.0f, ImGui.getFrameHeight()); // Make the lines as tall as when they have and input box ImGui.sameLine(0.0f, 0.0f); - ImGui.alignTextToFramePadding(); // Centers the node descriptions vertically in the frame height area - ImGui.getCursorScreenPos(lineMin); - lineMax.set(lineMin.x + ImGui.getContentRegionAvailX(), lineMin.y + ImGui.getFrameHeightWithSpacing()); + ImGui.getCursorScreenPos(indentMin); + lineMin.x = ImGui.getWindowContentRegionMin().x + ImGui.getWindowPosX(); + lineMin.y = indentMin.y; + lineMax.set(indentMin.x + ImGui.getContentRegionAvailX(), lineMin.y + ImGui.getFrameHeight()); mouseHoveringNodeLine = ImGuiTools.isItemHovered(ImGui.getContentRegionAvailX(), ImGui.getFrameHeight()); if (mouseHoveringNodeLine) - { ImGui.getWindowDrawList().addRectFilled(lineMin.x, lineMin.y, lineMax.x, lineMax.y, ImGui.getColorU32(ImGuiCol.MenuBarBg)); - } - } - - public void renderTreeViewIconArea() - { + float itemWidth = ImGui.getFontSize() * 1.0f; + if (!getChildren().isEmpty()) // expand/collapse arrow + { + float width = ImGui.getFontSize() / 2.5f; + float halfHeight = ImGui.getFrameHeight() * 0.5f / 2.0f; + boolean isHovered = ImGuiTools.isItemHovered(itemWidth, ImGui.getFrameHeight()); + int color = isHovered ? ImGui.getColorU32(ImGuiCol.ButtonHovered) : ImGui.getColorU32(ImGuiCol.Text); + if (treeWidgetExpanded) + { + float offsetX = ImGui.getCursorScreenPosX() + ImGui.getFontSize() * 0.2f; + float offsetY = ImGui.getCursorScreenPosY() + ImGui.getFrameHeight() * 0.4f; + ImGui.getWindowDrawList().addLine(offsetX, offsetY, + offsetX + halfHeight, offsetY + width, color); + ImGui.getWindowDrawList().addLine(offsetX + halfHeight, offsetY + width, + offsetX + halfHeight * 2.0f, offsetY, color); + } + else + { + float offsetX = ImGui.getCursorScreenPosX() + width; + float offsetY = ImGui.getCursorScreenPosY() + ImGui.getFrameHeight() * 0.2f; + ImGui.getWindowDrawList().addLine(offsetX + width, offsetY + halfHeight, + offsetX, offsetY + halfHeight * 2.0f, color); + ImGui.getWindowDrawList().addLine(offsetX + width, offsetY + halfHeight, + offsetX, offsetY, color); + } + if (isHovered && ImGui.isMouseClicked(ImGuiMouseButton.Left)) + { + anySpecificWidgetOnLineClicked = true; + treeWidgetExpanded = !treeWidgetExpanded; + } + } + ImGui.setCursorScreenPos(indentMin.x + itemWidth, indentMin.y); // Leave space for the expand/collapse arrow regardless } - public void renderNodeName() + public void renderEditableName() { String nameText = definition.getName(); if (definition.hasChanges()) nameText += "*"; + ImGui.setCursorScreenPos(indentMin.x + ImGui.getFontSize() * 3.0f, indentMin.y); + boolean textHovered = ImGuiTools.isItemHovered(ImGuiTools.calcTextSizeX(nameText), ImGui.getFrameHeight()); if (selected.get()) - { ImGui.getWindowDrawList().addRectFilled(lineMin.x, lineMin.y, lineMax.x, lineMax.y, ImGui.getColorU32(ImGuiCol.Header)); - } if (!isRootNode() && textHovered && ImGui.isMouseDoubleClicked(ImGuiMouseButton.Left)) { - setSpecificWidgetOnRowClicked(); + anySpecificWidgetOnLineClicked = true; RDXBehaviorTreeTools.clearOtherNodeSelections(this); selected.set(true); isNameBeingEdited = true; @@ -277,21 +310,11 @@ public boolean getSelected() return selected.get(); } - protected void setSpecificWidgetOnRowClicked() - { - anySpecificWidgetOnLineClicked = true; - } - public void setNameBeingEdited(boolean nameBeingEdited) { isNameBeingEdited = nameBeingEdited; } - public boolean getNameBeingEdited() - { - return isNameBeingEdited; - } - public int getNameColor() { return ImGui.getColorU32(ImGuiCol.Text); @@ -307,11 +330,6 @@ public boolean getTreeWidgetExpanded() return treeWidgetExpanded; } - public ImGuiVerticalAligner getChildrenDescriptionAligner() - { - return childrenDescriptionAligner; - } - public String getNodePopupID() { return nodePopupID; diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeRootNode.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeRootNode.java index aadbf188ed1b..ceb1c150c0e6 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeRootNode.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeRootNode.java @@ -85,16 +85,14 @@ public void updateSubtree(RDXBehaviorTreeNode node) } @Override - public void renderTreeViewIconArea() + public void renderTreeViewRow() { - super.renderTreeViewIconArea(); + super.renderRowBeginning(); + + rootIconWidget.render(); - if (rootIconWidget.render(!getChildren().isEmpty() && !getTreeWidgetExpanded())) - { - setSpecificWidgetOnRowClicked(); - setTreeWidgetExpanded(!getTreeWidgetExpanded()); - } ImGui.sameLine(); + super.renderEditableName(); } @Override diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java index 2161698e5a72..c0ce077ea8e6 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/behavior/tree/RDXBehaviorTreeWidgetsVerticalLayout.java @@ -32,12 +32,7 @@ public void renderImGuiWidgets(RDXBehaviorTreeNode node) { ImGui.pushStyleVar(ImGuiStyleVar.ItemSpacing, ImGui.getStyle().getItemSpacingX(), 0.0f); - node.renderGeneralRowBeginWidgets(); - node.renderTreeViewIconArea(); - -// if (node.getParent() != null) -// node.getParent().getChildrenDescriptionAligner().align(); - node.renderNodeName(); + node.renderTreeViewRow(); ImGui.popStyleVar(); @@ -115,7 +110,7 @@ public void renderImGuiWidgets(RDXBehaviorTreeNode node) if (node.getTreeWidgetExpanded()) { - float indentAmount = ImGui.getFontSize() * 0.5f; + float indentAmount = ImGui.getFontSize() * 0.7f; ImGui.indent(indentAmount); for (RDXBehaviorTreeNode child : node.getChildren()) diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java index f8c41f9fde55..fd56275b9ed3 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiFallbackWidget.java @@ -57,7 +57,7 @@ public ImGuiFallbackWidget() bottomPolygon[i] = new ImVec2(); } - public boolean render(boolean renderCollapsedArrow) + public boolean render() { float lineHeight = ImGui.getFrameHeight(); float fontSize = ImGui.getFontSize(); @@ -147,17 +147,6 @@ public boolean render(boolean renderCollapsedArrow) for (int i = 0; i < bottomPolygon.length - 1; i++) ImGui.getWindowDrawList().addLine(bottomPolygon[i].x, bottomPolygon[i].y, bottomPolygon[i + 1].x, bottomPolygon[i + 1].y, lineColor); - if (renderCollapsedArrow) // collapsed arrow - { - float offsetX = ImGui.getCursorScreenPosX() - scale * 0.6f; - float offsetY = ImGui.getCursorScreenPosY() + ImGui.getFrameHeight() * 0.2f; - float width = ImGui.getFontSize() / 2.5f; - float height = ImGui.getFrameHeight() * 0.5f; - int color = isHovered ? ImGui.getColorU32(ImGuiCol.ButtonHovered) : ImGui.getColorU32(ImGuiCol.Text); - ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY + height, color); - ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY, color); - } - ImGui.setCursorPosX(ImGui.getCursorPosX() + (itemWidth * 0.8f) + 0.1f * ImGui.getStyle().getItemSpacingX()); ImGui.newLine(); diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java index 02e9fbd6b605..bfaa78e51b27 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiRootIconWidget.java @@ -10,7 +10,7 @@ */ public class ImGuiRootIconWidget { - public boolean render(boolean renderCollapsedArrow) + public boolean render() { float lineHeight = ImGui.getFrameHeight(); float fontSize = ImGui.getFontSize(); @@ -49,17 +49,6 @@ public boolean render(boolean renderCollapsedArrow) ImGui.getWindowDrawList().addCircle((-wide * scale) + offsetX, (down * scale) + offsetY, circleSize * scale, lineColor); ImGui.getWindowDrawList().addCircle((wide * scale) + offsetX, (down * scale) + offsetY, circleSize * scale, lineColor); - if (renderCollapsedArrow) // collapsed arrow - { - offsetX = ImGui.getCursorScreenPosX(); - offsetY = ImGui.getCursorScreenPosY() + ImGui.getFrameHeight() * 0.2f; - float width = ImGui.getFontSize() / 2.5f; - float height = ImGui.getFrameHeight() * 0.5f; - int color = isHovered ? ImGui.getColorU32(ImGuiCol.ButtonHovered) : ImGui.getColorU32(ImGuiCol.Text); - ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY + height, color); - ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY, color); - } - ImGui.setCursorPosX(ImGui.getCursorPosX() + itemWidth); ImGui.newLine(); return isHovered && ImGui.isMouseClicked(ImGuiMouseButton.Left); diff --git a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSequenceIconWidget.java b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSequenceIconWidget.java index 20f1fa4ba3d1..98180d79258d 100644 --- a/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSequenceIconWidget.java +++ b/ihmc-high-level-behaviors/src/libgdx/java/us/ihmc/rdx/ui/widgets/ImGuiSequenceIconWidget.java @@ -10,7 +10,7 @@ */ public class ImGuiSequenceIconWidget { - public boolean render(boolean renderCollapsedArrow) + public boolean render() { float lineHeight = ImGui.getFrameHeight(); float fontSize = ImGui.getFontSize(); @@ -144,17 +144,6 @@ public boolean render(boolean renderCollapsedArrow) offsetY + scale * baseBottomRightY, lineColor); - if (renderCollapsedArrow) // collapsed arrow - { - offsetX = ImGui.getCursorScreenPosX(); - offsetY = ImGui.getCursorScreenPosY() + ImGui.getFrameHeight() * 0.2f; - float width = ImGui.getFontSize() / 2.5f; - float height = ImGui.getFrameHeight() * 0.5f; - int color = isHovered ? ImGui.getColorU32(ImGuiCol.ButtonHovered) : ImGui.getColorU32(ImGuiCol.Text); - ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY + height, color); - ImGui.getWindowDrawList().addLine(offsetX, offsetY + height / 2.0f, offsetX - width, offsetY, color); - } - ImGui.setCursorPosX(ImGui.getCursorPosX() + itemWidth); ImGui.newLine(); return isHovered && ImGui.isMouseClicked(ImGuiMouseButton.Left);