Browse Source

A working Node Graph :v:

Coldzer0 1 year ago
parent
commit
827ab9f26d
2 changed files with 716 additions and 0 deletions
  1. 366 0
      examples/CustomNodeGraph.pas
  2. 350 0
      examples/testwindow.pas

+ 366 - 0
examples/CustomNodeGraph.pas

@@ -0,0 +1,366 @@
+{
+  FreePascal / Delphi bindings for ImGui
+
+  Copyright (C) 2023 Coldzer0 <Coldzer0 [at] protonmail.ch>
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the MIT License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  MIT License for more details.
+}
+
+// based on https://gist.github.com/ocornut/7e9b3ec566a333d725d4
+
+Unit CustomNodeGraph;
+
+{$IfDef FPC}
+  {$mode Delphi}{$H+}
+  {$ModeSwitch advancedrecords}
+{$EndIf}
+
+Interface
+
+Uses
+  Generics.Collections,
+  SysUtils,
+  PasImGui,
+  PasImGui.Utils;
+
+Type
+  { TNode }
+
+  TNode = Record
+    ID: Integer;
+    Name: Array [0..31] Of AnsiChar;
+    Pos, Size: ImVec2;
+    Value: Single;
+    Color: ImVec4;
+    InputsCount, OutputsCount: Integer;
+    Function GetInputSlotPos(slotNo: Integer): ImVec2;
+    Function GetOutputSlotPos(slotNo: Integer): ImVec2;
+    Constructor Create(id_: Integer; Const Name_: PAnsiChar;
+      pos_: ImVec2; Value_: Single; color_: ImVec4; inputsCount_, outputsCount_: Integer);
+  End;
+
+  { TNodeLink }
+
+  TNodeLink = Record
+    InputIdx, InputSlot, OutputIdx, OutputSlot: Integer;
+    Constructor Create(inputIdx_, inputSlot_, outputIdx_, outputSlot_: Integer);
+  End;
+
+Procedure ShowExampleAppCustomNodeGraph(opened: PBoolean);
+
+Implementation
+
+Uses
+  Math;
+
+Var
+  nodes: TList<TNode>;
+  links: TList<TNodeLink>;
+  scrolling: ImVec2 = (x: 0.0; y: 0.0);
+  inited: Boolean = False;
+  show_grid: Boolean = False;
+  node_selected: Integer = -1;
+
+  test : Single = 0.0;
+
+Procedure ShowExampleAppCustomNodeGraph(opened: PBoolean);
+Var
+  io: PImGuiIO;
+  node, node_inp, node_out: TNode;
+  link: TNodeLink;
+  node_idx, slot_idx, link_idx: Integer;
+  offset, win_pos, canvas_sz, p1, p2, node_rect_min, node_rect_max, scene_pos: ImVec2;
+  draw_list: PImDrawList;
+  GRID_COLOR, node_bg_color: ImU32;
+  GRID_SZ, x, y: Single;
+  old_any_active, node_widgets_active, node_moving_active: Boolean;
+  open_context_menu: Boolean;
+  node_hovered_in_list: Integer;
+  node_hovered_in_scene: Integer;
+Const
+  NODE_SLOT_RADIUS: Single = 4.0;
+  NODE_WINDOW_PADDING: ImVec2 = (x: 8.0; y: 8.0);
+Begin
+  ImGui.SetNextWindowSize(ImVec2.New(800, 600), ImGuiCond_FirstUseEver);
+  //ImGui.SetNextWindowPosCenter(ImGuiCond_FirstUseEver);
+  If Not ImGui.Begin_('Example: Custom Node Graph', opened) Then
+  Begin
+    ImGui.End_;  // Early out if the window is collapsed, as an optimization.
+    exit;
+  End;
+
+  io := ImGui.GetIO();
+
+  If Not inited Then
+  Begin
+    nodes := TList<TNode>.Create();
+    links := TList<TNodeLink>.Create();
+
+    nodes.Add(TNode.Create(0, 'MainTex', ImVec2.New(40, 50), 0.5, ImVec4.New(255, 100, 100), 1, 1));
+    nodes.Add(TNode.Create(1, 'BumpMap', ImVec2.New(40, 150), 0.42, ImVec4.New(200, 100, 200), 1, 1));
+    nodes.Add(TNode.Create(2, 'Combine', ImVec2.New(270, 80), 1.0, ImVec4.New(0, 200, 100), 2, 2));
+    links.Add(TNodeLink.Create(0, 0, 2, 0));
+    links.Add(TNodeLink.Create(1, 0, 2, 1));
+
+    inited := True;
+  End;
+  open_context_menu := False;
+  node_hovered_in_list := -1;
+  node_hovered_in_scene := -1;
+
+  // Draw a list of nodes on the left side
+  ImGui.BeginChild('node_list', ImVec2.New(100, 0));
+  ImGui.Text('Nodes');
+  ImGui.Separator();
+
+  For node_idx := 0 To Pred(nodes.Count) Do
+  Begin
+    node := nodes[node_idx];
+    ImGui.PushIdInt(node.ID);
+    If ImGui.Selectable(node.Name, node.ID = node_selected) Then
+      node_selected := node.ID;
+
+    If ImGui.IsItemHovered() Then
+    Begin
+      node_hovered_in_list := node.ID;
+      open_context_menu := Boolean(Ord(open_context_menu) Or
+        Ord(ImGui.IsMouseClicked(ImGuiMouseButton_Left)));
+    End;
+    ImGui.PopID();
+  End;
+  ImGui.EndChild();
+
+  ImGui.SameLine();
+  ImGui.BeginGroup();
+
+  // Create our child canvas
+  ImGui.Text('Hold middle mouse button to scroll (%.2f,%.2f)',[scrolling.x, scrolling.y]);
+  ImGui.SameLine();
+  ImGui.Checkbox('Show grid', @show_grid);
+  ImGui.PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2.New(1, 1));
+  ImGui.PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2.New(0, 0));
+  ImGui.PushStyleColor(ImGuiCol_ChildBg, IM_COL32(60, 60, 70, 200));
+  ImGui.BeginChild('scrolling_region', ImVec2.New(0, 0), ImGuiChildFlags_None,
+    ImGuiWindowFlags_NoScrollbar Or ImGuiWindowFlags_NoMove);
+  ImGui.PopStyleVar(); // WindowPadding
+  ImGui.PushItemWidth(120.0);
+
+  offset := ImGui.GetCursorScreenPos() + scrolling;
+  draw_list := ImGui.GetWindowDrawList();
+
+  // Display grid
+  If show_grid Then
+  Begin
+    GRID_COLOR := IM_COL32(200, 200, 200, 40);
+    GRID_SZ := 64.0;
+    win_pos := ImGui.GetCursorScreenPos();
+    canvas_sz := ImGui.GetWindowSize();
+
+    x := FMod(scrolling.x, GRID_SZ);
+    while x < canvas_sz.x do
+    begin
+      draw_list^.AddLine(ImVec2.New(x, 0.0) + win_pos, ImVec2.New(x, canvas_sz.y) + win_pos, GRID_COLOR);
+      x := x + GRID_SZ;
+    end;
+
+    y := fmod(scrolling.y, GRID_SZ);
+    While y < canvas_sz.y Do
+    Begin
+      draw_list^.AddLine(ImVec2.New(0.0, y) + win_pos, ImVec2.New(canvas_sz.x, y) + win_pos, GRID_COLOR);
+      y := y + GRID_SZ;
+    End;
+  End;
+
+  // Display links
+  draw_list^.ChannelsSplit(2);
+  draw_list^.ChannelsSetCurrent(0); // Background
+
+  For link_idx := 0 To Pred(links.Count) Do
+  Begin
+    link := links[link_idx];
+    node_inp := nodes[link.InputIdx];
+    node_out := nodes[link.OutputIdx];
+    p1 := offset + node_inp.GetOutputSlotPos(link.InputSlot);
+    p2 := offset + node_out.GetInputSlotPos(link.OutputSlot);
+    draw_list^.AddBezierCubic(p1, p1 + ImVec2.New(-50, 0), p2 + ImVec2.New(+50, 0), p2, IM_COL32(200, 200, 100, 255), 3.0);
+  End;
+
+  // Display nodes
+  For node_idx := 0 To Pred(nodes.Count) Do
+  Begin
+    node := nodes[node_idx];
+
+    ImGui.PushIdInt(node.ID);
+    node_rect_min := offset + node.Pos;
+
+    // Display node contents first
+    draw_list^.ChannelsSetCurrent(1); // Foreground
+    old_any_active := ImGui.IsAnyItemActive();
+    ImGui.SetCursorScreenPos(node_rect_min + NODE_WINDOW_PADDING);
+    ImGui.BeginGroup(); // Lock horizontal position
+    ImGui.Text('%s', [node.Name]);
+    ImGui.SliderFloat('##value', @node.Value, 0.0, 1.0, 'Alpha %.2f');
+    ImGui.ColorEdit3('##color', @node.Color.x);
+    ImGui.EndGroup();
+
+    // Save the size of what we have emitted and whether any of the widgets are being used
+    node_widgets_active := (Not old_any_active And ImGui.IsAnyItemActive());
+    node.Size := ImGui.GetItemRectSize() + NODE_WINDOW_PADDING + NODE_WINDOW_PADDING;
+    node_rect_max := node_rect_min + node.Size;
+
+    // Display node box
+    draw_list^.ChannelsSetCurrent(0); // Background
+    ImGui.SetCursorScreenPos(node_rect_min);
+    ImGui.InvisibleButton('node', node.Size);
+    If ImGui.IsItemHovered() Then
+    Begin
+      node_hovered_in_scene := node.ID;
+      open_context_menu := open_context_menu Or ImGui.IsMouseClicked(1);
+    End;
+    node_moving_active := ImGui.IsItemActive();
+
+    If (node_widgets_active Or node_moving_active) Then
+      node_selected := node.ID;
+
+    If (node_moving_active And ImGui.IsMouseDragging(ImGuiMouseButton_Left)) Then
+      node.Pos := node.Pos + io^.MouseDelta;
+
+    If ((node_hovered_in_list = node.ID) Or (node_hovered_in_scene = node.ID) Or
+      ((node_hovered_in_list = -1) And (node_selected = node.ID))) Then
+      node_bg_color := IM_COL32(75, 75, 75, 255)
+    Else
+      node_bg_color := IM_COL32(60, 60, 60, 255);
+
+    draw_list^.AddRectFilled(node_rect_min, node_rect_max, node_bg_color, 4.0);
+    draw_list^.AddRect(node_rect_min, node_rect_max, IM_COL32(100, 100, 100, 255), 4.0);
+
+    For slot_idx := 0 To Pred(node.InputsCount) Do
+      draw_list^.AddCircleFilled(offset + node.GetInputSlotPos(slot_idx),
+        NODE_SLOT_RADIUS, IM_COL32(150, 150, 150, 150));
+    For slot_idx := 0 To Pred(node.OutputsCount) Do
+      draw_list^.AddCircleFilled(offset + node.GetOutputSlotPos(slot_idx),
+        NODE_SLOT_RADIUS, IM_COL32(150, 150, 150, 150));
+
+    ImGui.PopID();
+    nodes[node_idx] := node;
+  End;
+  draw_list^.ChannelsMerge();
+
+  // Open context menu
+  If ImGui.IsMouseReleased(ImGuiMouseButton_Right) Then
+  Begin
+    If ImGui.IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup) Or
+      (Not ImGui.IsAnyItemHovered()) Then
+    Begin
+      node_hovered_in_list := -1;
+      node_hovered_in_scene := -1;
+      node_selected := -1;
+      open_context_menu := True;
+    End;
+  End;
+  If open_context_menu Then
+  Begin
+    ImGui.OpenPopup('context_menu');
+    If (node_hovered_in_list <> -1) Then
+      node_selected := node_hovered_in_list;
+    If (node_hovered_in_scene <> -1) Then
+      node_selected := node_hovered_in_scene;
+  End;
+  // Draw context menu
+  ImGui.PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2.New(8, 8));
+  If ImGui.BeginPopup('context_menu') Then
+  Begin
+    scene_pos := ImGui.GetMousePosOnOpeningCurrentPopup() - offset;
+
+    If node_selected <> -1 Then
+    Begin
+      node := nodes[node_selected];
+      ImGui.Text('Node "%s"', [node.Name]);
+      ImGui.Separator();
+      If ImGui.MenuItem('Rename..', nil, False, False) Then
+      Begin
+      End;
+      If ImGui.MenuItem('Delete', nil, False, False) Then
+      Begin
+      End;
+      If ImGui.MenuItem('Copy', nil, False, False) Then
+      Begin
+      End;
+    End
+    Else
+    Begin
+      If ImGui.MenuItem('Add') Then
+        nodes.Add(Node.Create(nodes.Count, 'New node', scene_pos, 0.5,
+          ImVec4.New(100, 100, 200), 2, 2));
+      If ImGui.MenuItem('Past', nil, False, False) Then
+      Begin
+      End;
+    End;
+    ImGui.EndPopup();
+  End;
+  ImGui.PopStyleVar();
+
+  // Scrolling
+  If (ImGui.IsWindowHovered() And (Not ImGui.IsAnyItemActive()) And
+    ImGui.IsMouseDragging(ImGuiMouseButton_Middle, 0.0)) Then
+    scrolling := scrolling + io.MouseDelta;
+
+  ImGui.PopItemWidth();
+  ImGui.EndChild();
+  ImGui.PopStyleColor();
+  ImGui.PopStyleVar();
+  ImGui.EndGroup();
+
+  ImGui.End_();
+End;
+
+{ TNode }
+
+Function TNode.GetInputSlotPos(slotNo: Integer): ImVec2;
+Begin
+  Result.x := Pos.x;
+  Result.y := Pos.y + Size.y * Single(slotNo + 1) / (Single(InputsCount) + 1);
+End;
+
+Function TNode.GetOutputSlotPos(slotNo: Integer): ImVec2;
+Begin
+  Result.x := Pos.x + Size.x;
+  Result.y := Pos.y + Size.y * Single(slotNo + 1) / (Single(OutputsCount) + 1);
+End;
+
+Constructor TNode.Create(id_: Integer; Const Name_: PAnsiChar;
+  pos_: ImVec2; Value_: Single; color_: ImVec4; inputsCount_, outputsCount_: Integer);
+Begin
+  Self.ID := id_;
+  StrLCopy(Self.Name, name_, SizeOf(Self.Name) - 1);
+  Self.Pos := pos_;
+  Self.Value := value_;
+  Self.Color := color_;
+  Self.InputsCount := inputsCount_;
+  Self.OutputsCount := outputsCount_;
+End;
+
+{ TNodeLink }
+
+Constructor TNodeLink.Create(inputIdx_, inputSlot_, outputIdx_, outputSlot_: Integer);
+Begin
+  Self.InputIdx := inputIdx_;
+  Self.InputSlot := inputSlot_;
+  Self.OutputIdx := outputIdx_;
+  Self.OutputSlot := outputSlot_;
+End;
+
+Initialization
+
+
+Finalization
+  nodes.Free;
+  links.Free;
+
+End.

+ 350 - 0
examples/testwindow.pas

@@ -0,0 +1,350 @@
+{
+  FreePascal / Delphi bindings for ImGui
+
+  Copyright (C) 2023 Coldzer0 <Coldzer0 [at] protonmail.ch>
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the MIT License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  MIT License for more details.
+}
+
+{
+  Code From https://github.com/dpethes/imgui-pas - Updated by Coldzer0
+
+  Partial translation of imgui demo / ShowTestWindow
+  While you're probably better off with the original version as it's way more extensive,
+  this is good as
+    * a test case for the bindings or
+    * a quick guide if something isn't translated in a straightforward way
+}
+Unit TestWindow;
+
+{$IFDEF FPC}
+  {$mode objfpc}{$H+}
+{$ENDIF}
+
+{$J+}
+
+Interface
+
+Uses
+  Classes,
+  SysUtils,
+  PasImGui,
+  PasImGui.Utils;
+
+Type
+
+  { TTestWindow }
+  TTestWindow = Class
+  Private
+    show_app_main_menu_bar: boolean;
+    show_app_console: boolean;
+    show_app_log: boolean;
+    show_app_layout: boolean;
+    show_app_property_editor: boolean;
+    show_app_long_text: boolean;
+    show_app_auto_resize: boolean;
+    show_app_constrained_resize: boolean;
+    show_app_fixed_overlay: boolean;
+    show_app_manipulating_window_title: boolean;
+    show_app_custom_rendering: boolean;
+    show_app_style_editor: boolean;
+
+    show_app_metrics: boolean;
+    show_app_about: boolean;
+
+    no_titlebar: boolean;
+    no_resize: boolean;
+    no_move: boolean;
+    no_scrollbar: boolean;
+    no_collapse: boolean;
+    no_menu: boolean;
+
+    Procedure Trees;
+  Public
+    Constructor Create;
+    Procedure Show(Var p_open: boolean);
+  End;
+
+Implementation
+
+Procedure ShowHelpMarker(Const desc: AnsiString);
+Begin
+  ImGui.TextDisabled('(?)');
+  If (ImGui.IsItemHovered()) Then
+  Begin
+    ImGui.BeginTooltip();
+    ImGui.PushTextWrapPos(450.0);
+    ImGui.TextUnformatted(desc);
+    ImGui.PopTextWrapPos();
+    ImGui.EndTooltip();
+  End;
+End;
+
+{ TTestWindow }
+
+Procedure TTestWindow.Trees;
+Const  //static vars
+  align_label_with_current_x_position: boolean = False;
+  selection_mask: integer = 1 shl 2;
+  // Dumb representation of what may be user-side selection state. You may carry selection state inside or outside your objects in whatever format you see fit.
+Var
+  node_open: Boolean;
+  node_clicked: Integer;
+  i: Integer;
+  node_flags: ImGuiTreeNodeFlags;
+Begin
+  If (ImGui.TreeNode('Basic trees')) Then
+  Begin
+    For i := 0 To 4 Do
+      If (ImGui.TreeNode({%H-}PImGuiID(i), 'Child %d', [i])) Then
+      Begin
+        ImGui.Text('blah blah');
+        ImGui.SameLine();
+        If (ImGui.SmallButton('print')) Then
+        begin
+          ImGui.LogText('Child ' + i.ToString + ' pressed');
+          Imgui.LogFinish;
+        end;
+        ImGui.TreePop();
+      End;
+    ImGui.TreePop();
+  End;
+
+  If (ImGui.TreeNode('Advanced, with Selectable nodes')) Then
+  Begin
+    ShowHelpMarker('This is a more standard looking tree with selectable nodes.' + #10 +
+      'Click to select, CTRL+Click to toggle, click on arrows or double-click to open.');
+    ImGui.Checkbox('Align label with current X position)',
+      @align_label_with_current_x_position);
+    ImGui.Text('Hello!');
+    If (align_label_with_current_x_position) Then
+      ImGui.Unindent(ImGui.GetTreeNodeToLabelSpacing());
+    node_clicked := -1;
+    // Temporary storage of what node we have clicked to process selection at the end of the loop. May be a pointer to your own node type, etc.
+    ImGui.PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui.GetFontSize() * 3);
+    // Increase spacing to differentiate leaves from expanded contents.
+    For i := 0 To 5 Do
+    Begin
+      // Disable the default open on single-click behavior and pass in Selected flag according to our selection state.
+      //ImGuiTreeNodeFlags node_flags := ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ((selection_mask & (1 << i)) ? ImGuiTreeNodeFlags_Selected : 0);
+      node_flags := ImGuiTreeNodeFlags_OpenOnArrow Or
+        ImGuiTreeNodeFlags_OpenOnDoubleClick;
+      If (selection_mask And (1 shl i)) > 0 Then
+        node_flags := node_flags Or ImGuiTreeNodeFlags_Selected;
+      If (i < 3) Then
+      Begin
+        // Node
+        node_open := ImGui.TreeNodeEx({%H-}PImGuiID(i), node_flags,
+          'Selectable Node %d', [i]);
+        If (ImGui.IsItemClicked()) Then
+          node_clicked := i;
+        If (node_open) Then
+        Begin
+          ImGui.Text('Blah blah' + #10 + 'Blah Blah');
+          ImGui.TreePop();
+        End;
+      End
+      Else
+      Begin
+        // Leaf: The only reason we have a TreeNode at all is to allow selection of the leaf. Otherwise we can use BulletText() or TreeAdvanceToLabelPos()+Text().
+        node_flags := node_flags Or ImGuiTreeNodeFlags_Leaf Or
+          ImGuiTreeNodeFlags_NoTreePushOnOpen;
+        ImGui.TreeNodeEx({%H-}PImGuiID(i), node_flags, 'Selectable Leaf %d', [i]);
+        If (ImGui.IsItemClicked()) Then
+          node_clicked := i;
+      End;
+    End;
+    If (node_clicked <> -1) Then
+    Begin
+      // Update selection state. Process outside of tree loop to avoid visual inconsistencies during the clicking-frame.
+      If (ImGui.GetIO()^.KeyCtrl) Then
+        selection_mask := selection_mask Xor (1 shl node_clicked)          // CTRL+click to toggle
+      Else
+        //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection
+        selection_mask := (1 shl node_clicked);           // Click to single-select
+    End;
+    ImGui.PopStyleVar();
+    If (align_label_with_current_x_position) Then
+      ImGui.Indent(ImGui.GetTreeNodeToLabelSpacing());
+    ImGui.TreePop();
+  End;
+  ImGui.TreePop();
+End;
+
+Constructor TTestWindow.Create;
+Begin
+  show_app_main_menu_bar := False;
+  show_app_console := False;
+  show_app_log := False;
+  show_app_layout := False;
+  show_app_property_editor := False;
+  show_app_long_text := False;
+  show_app_auto_resize := False;
+  show_app_constrained_resize := False;
+  show_app_fixed_overlay := False;
+  show_app_manipulating_window_title := False;
+  show_app_custom_rendering := False;
+  show_app_style_editor := False;
+
+  show_app_metrics := False;
+  show_app_about := False;
+
+  no_titlebar := False;
+  no_resize := False;
+  no_move := False;
+  no_scrollbar := False;
+  no_collapse := False;
+  no_menu := False;
+End;
+
+Procedure TTestWindow.Show(Var p_open: boolean);
+Var
+  io: PImGuiIO;
+  window_flags: ImGuiWindowFlags;
+  draw_list: PImDrawList;
+  value_raw, value_with_lock_threshold, mouse_delta: ImVec2;
+Begin
+  window_flags := ImGuiWindowFlags_None;
+  // Demonstrate the various window flags. Typically you would just use the default.
+  If (no_titlebar) Then window_flags := window_flags Or ImGuiWindowFlags_NoTitleBar;
+  If (no_resize) Then window_flags := window_flags Or ImGuiWindowFlags_NoResize;
+  If (no_move) Then window_flags := window_flags Or ImGuiWindowFlags_NoMove;
+  If (no_scrollbar) Then window_flags := window_flags Or ImGuiWindowFlags_NoScrollbar;
+  If (no_collapse) Then window_flags := window_flags Or ImGuiWindowFlags_NoCollapse;
+  If (Not no_menu) Then window_flags := window_flags Or ImGuiWindowFlags_MenuBar;
+
+  ImGui.SetNextWindowSize(ImVec2.New(550, 680), ImGuiCond_FirstUseEver);
+  If Not ImGui.Begin_('ImGui Demo (FreePascal / Delphi version)', @p_open, window_flags) Then
+  Begin
+    // Early out if the window is collapsed, as an optimization.
+    ImGui.End_;
+    exit;
+  End;
+
+  //ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f);    // 2/3 of the space for widget and 1/3 for labels
+  ImGui.PushItemWidth(ImGui.GetWindowWidth() * 0.65);
+  // Right align, keep 140 pixels for labels
+
+  ImGui.Text('Dear ImGui says hello.');
+
+  // Menu
+  If (ImGui.BeginMenuBar()) Then
+  Begin
+    If (ImGui.BeginMenu('Menu')) Then
+    Begin
+      ImGui.MenuItem('Open');
+      ImGui.MenuItem('Close');
+      ImGui.EndMenu();
+    End;
+    If (ImGui.BeginMenu('Examples')) Then
+    Begin
+      ImGui.MenuItem('Main menu bar', nil, @show_app_main_menu_bar);
+      ImGui.MenuItem('Console', nil, @show_app_console);
+      ImGui.MenuItem('Log', nil, @show_app_log);
+      ImGui.MenuItem('Simple layout', nil, @show_app_layout);
+      ImGui.MenuItem('Property editor', nil, @show_app_property_editor);
+      ImGui.MenuItem('Long text display', nil, @show_app_long_text);
+      ImGui.MenuItem('Auto-resizing window', nil, @show_app_auto_resize);
+      ImGui.MenuItem('Constrained-resizing window', nil,
+        @show_app_constrained_resize);
+      ImGui.MenuItem('Simple overlay', nil, @show_app_fixed_overlay);
+      ImGui.MenuItem('Manipulating window title', nil,
+        @show_app_manipulating_window_title);
+      ImGui.MenuItem('Custom rendering', nil, @show_app_custom_rendering);
+      ImGui.EndMenu();
+    End;
+    If (ImGui.BeginMenu('Help')) Then
+    Begin
+      ImGui.MenuItem('Metrics', nil, @show_app_metrics);
+      ImGui.MenuItem('Style Editor', nil, @show_app_style_editor);
+      ImGui.MenuItem('About ImGui', nil, @show_app_about);
+      ImGui.EndMenu();
+    End;
+    ImGui.EndMenuBar();
+  End;
+
+  ImGui.Spacing();
+  If ImGui.CollapsingHeader('Help') Then
+  Begin
+    ImGui.TextWrapped(
+      'This window is being created by the ShowTestWindow() function. Please refer to the code for programming reference.'
+      + #10#10 +
+      'User Guide:');
+    ImGui.ShowUserGuide();
+  End;
+
+  If ImGui.CollapsingHeader('Window options') Then
+  Begin
+    ImGui.Checkbox('No titlebar', @no_titlebar);
+    ImGui.SameLine(150);
+    ImGui.Checkbox('No resize', @no_resize);
+    ImGui.Checkbox('No move', @no_move);
+    ImGui.SameLine(150);
+    ImGui.Checkbox('No scrollbar', @no_scrollbar);
+    ImGui.SameLine(300);
+    ImGui.Checkbox('No collapse', @no_collapse);
+    ImGui.Checkbox('No menu', @no_menu);
+
+    If ImGui.TreeNode('Style') Then
+    Begin
+      ImGui.ShowStyleEditor(Imgui.GetStyle());
+      //this is useful to have, but doesn't need to be translated as an example
+      ImGui.TreePop();
+    End;
+
+    If ImGui.TreeNode('Logging') Then
+    Begin
+      ImGui.TextWrapped(
+        'The logging API redirects all text output so you can easily capture the content of a window or a block. Tree nodes can be automatically expanded. You can also call ImGui.LogText() to output directly to the log without a visual output.');
+      ImGui.LogButtons();
+      ImGui.TreePop();
+    End;
+  End;
+
+  If ImGui.CollapsingHeader('Widgets') Then
+  Begin
+    If ImGui.TreeNode('Trees') Then
+      Trees;
+  End;
+
+  If ImGui.CollapsingHeader('Keyboard, Mouse & Focus') Then
+  Begin
+    If ImGui.TreeNode('Dragging') Then
+    Begin
+      ImGui.TextWrapped(
+        'You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget.');
+      ImGui.Button('Drag Me');
+      If ImGui.IsItemActive() Then
+      Begin
+        io := ImGui.GetIO();
+        // Draw a line between the button and the mouse cursor
+        draw_list := ImGui.GetWindowDrawList();
+        draw_list^.PushClipRectFullScreen;
+        draw_list^.AddLine(io^.MouseClickedPos[0], io^.MousePos,
+          ImGui.GetColorU32Vec(ImGui.GetStyle()^.Colors[ImGuiCol_DragDropTarget]),
+          4.0);
+        draw_list^.PopClipRect;
+
+        value_raw := ImGui.GetMouseDragDelta(ImGuiMouseButton_Left, 0.0);
+        value_with_lock_threshold := ImGui.GetMouseDragDelta(ImGuiMouseButton_Left);
+        mouse_delta := ImGui.GetIO()^.MouseDelta;
+        ImGui.SameLine();
+        ImGui.TextWrapped(
+          'Raw (%.1f, %.1f), WithLockThresold (%.1f, %.1f), MouseDelta (%.1f, %.1f)',
+          [value_raw.x, value_raw.y, value_with_lock_threshold.x,
+          value_with_lock_threshold.y, mouse_delta.x, mouse_delta.y]);
+      End;
+      ImGui.TreePop();
+    End;
+  End;
+
+  ImGui.End_;
+End;
+
+End.