2025-09-11 18:18:13 +02:00
|
|
|
import strformat, strutils
|
2025-09-10 18:25:15 +02:00
|
|
|
import imguin/[cimgui, glfw_opengl, simple]
|
|
|
|
|
import ../utils/appImGui
|
|
|
|
|
import ../../common/[types]
|
|
|
|
|
|
|
|
|
|
type
|
|
|
|
|
ConsoleComponent* = ref object of RootObj
|
|
|
|
|
agent: Agent
|
|
|
|
|
showConsole*: bool
|
2025-09-11 18:18:13 +02:00
|
|
|
inputBuffer: string
|
|
|
|
|
consoleEntries: seq[string]
|
2025-09-11 19:11:11 +02:00
|
|
|
console: ptr TextEditor
|
2025-09-10 18:25:15 +02:00
|
|
|
|
|
|
|
|
proc Console*(agent: Agent): ConsoleComponent =
|
|
|
|
|
result = new ConsoleComponent
|
|
|
|
|
result.agent = agent
|
|
|
|
|
result.showConsole = true
|
2025-09-11 19:11:11 +02:00
|
|
|
result.console = TextEditor_TextEditor()
|
2025-09-11 18:18:13 +02:00
|
|
|
result.consoleEntries = @[
|
2025-09-12 10:15:13 +02:00
|
|
|
"a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a",
|
2025-09-11 19:11:11 +02:00
|
|
|
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
2025-09-11 18:18:13 +02:00
|
|
|
]
|
|
|
|
|
|
2025-09-11 19:11:11 +02:00
|
|
|
result.console.TextEditor_SetText(result.consoleEntries.join("\n") & '\0')
|
|
|
|
|
|
2025-09-11 18:18:13 +02:00
|
|
|
proc findLongestLength(text: string): float32 =
|
|
|
|
|
var maxWidth = 0.0f
|
|
|
|
|
for line in text.splitLines():
|
|
|
|
|
let line_cstring = line.cstring
|
|
|
|
|
var textSizeOut: ImVec2
|
|
|
|
|
igCalcTextSize(addr textSizeOut, line_cstring, nil, false, -1.0f)
|
|
|
|
|
if textSizeOut.x > maxWidth:
|
|
|
|
|
maxWidth = textSizeOut.x
|
|
|
|
|
return maxWidth
|
2025-09-10 18:25:15 +02:00
|
|
|
|
|
|
|
|
proc draw*(component: ConsoleComponent) =
|
|
|
|
|
|
|
|
|
|
igBegin(fmt"[{component.agent.agentId}] {component.agent.username}@{component.agent.hostname}", addr component.showConsole, 0)
|
|
|
|
|
defer: igEnd()
|
|
|
|
|
|
2025-09-12 10:15:13 +02:00
|
|
|
#[
|
|
|
|
|
Console entries/text section
|
|
|
|
|
|
|
|
|
|
Problems:
|
|
|
|
|
# A InputTextMultiline component is placed within a Child Frame to enable both proper text selection and a horizontal scrollbar
|
|
|
|
|
# The only thing missing from this implementation is the ability change the text color and auto-scrolling
|
|
|
|
|
# https://github.com/ocornut/imgui/issues/383#issuecomment-2080346129
|
|
|
|
|
# https://github.com/ocornut/imgui/issues/950
|
|
|
|
|
]#
|
|
|
|
|
let footerHeight = igGetStyle().ItemSpacing.y + igGetFrameHeightWithSpacing() # * 2
|
2025-09-11 18:18:13 +02:00
|
|
|
let buffer = component.consoleEntries.join("\n") & '\0'
|
|
|
|
|
|
|
|
|
|
# Push styles to hide the Child's background and scrollbar background.
|
|
|
|
|
igPushStyleColor_Vec4(ImGuiCol_FrameBg.int32, vec4(0.0f, 0.0f, 0.0f, 0.0f))
|
|
|
|
|
igPushStyleColor_Vec4(ImGuiCol_ScrollbarBg.int32, vec4(0.0f, 0.0f, 0.0f, 0.0f))
|
|
|
|
|
|
|
|
|
|
if igBeginChild_Str("##Console", vec2(-0.99f, -footerHeight), ImGuiChildFlags_NavFlattened.int32, ImGuiWindowFlags_HorizontalScrollbar.int32):
|
|
|
|
|
|
|
|
|
|
# Manually handle horizontal scrolling with the mouse wheel/touchpad
|
|
|
|
|
let io = igGetIO()
|
|
|
|
|
if io.MouseWheelH != 0:
|
|
|
|
|
let scroll_delta = io.MouseWheelH * igGetScrollX() * 0.5
|
|
|
|
|
igSetScrollX_Float(igGetScrollX() - scroll_delta)
|
|
|
|
|
if igGetScrollX() == 0:
|
|
|
|
|
igSetScrollX_Float(1.0f) # This is required to prevent the horizontal scrolling from snapping in
|
|
|
|
|
|
|
|
|
|
# Retrieve the length of the longes console entry
|
2025-09-11 19:11:11 +02:00
|
|
|
var width = findLongestLength(buffer)
|
|
|
|
|
if width <= io.DisplaySize.x:
|
|
|
|
|
width = -1.0f
|
2025-09-11 18:18:13 +02:00
|
|
|
|
|
|
|
|
# Set the Text edit background color and make it visible.
|
|
|
|
|
igPushStyleColor_Vec4(ImGuiCol_FrameBg.int32, vec4(0.1f, 0.1f, 0.1f, 1.0f))
|
|
|
|
|
igPushStyleColor_Vec4(ImGuiCol_ScrollbarBg.int32, vec4(0.1f, 0.1f, 0.1f, 1.0f))
|
|
|
|
|
discard igInputTextMultiline("##ConsoleText", buffer, cast[csize_t](buffer.len()), vec2(width, -1.0f), ImGui_InputTextFlags_ReadOnly.int32 or ImGui_InputTextFlags_AllowTabInput.int32, nil, nil)
|
2025-09-11 19:11:11 +02:00
|
|
|
|
|
|
|
|
# Alternative: ImGuiColorTextEdit
|
|
|
|
|
# component.console.TextEditor_SetReadOnlyEnabled(true)
|
|
|
|
|
# component.console.TextEditor_SetShowLineNumbersEnabled(false)
|
|
|
|
|
# component.console.TextEditor_Render("##ConsoleEntries", false, vec2(-1, -1), true)
|
|
|
|
|
|
2025-09-12 10:15:13 +02:00
|
|
|
# # Scroll to bottom
|
|
|
|
|
# if igGetScrollY() >= igGetScrollMaxY():
|
|
|
|
|
# let lineCount = component.console.TextEditor_GetLineCount()
|
|
|
|
|
# component.console.TextEditor_SetCursorPosition(lineCount, 0)
|
|
|
|
|
|
2025-09-11 18:18:13 +02:00
|
|
|
igPopStyleColor(2)
|
|
|
|
|
|
|
|
|
|
igPopStyleColor(2)
|
|
|
|
|
igEndChild()
|
|
|
|
|
|
2025-09-12 10:15:13 +02:00
|
|
|
#[
|
|
|
|
|
Input field with prompt indicator
|
|
|
|
|
]#
|
|
|
|
|
let promptIndicator = fmt"[{component.agent.agentId}]"
|
|
|
|
|
var charWidth: ImVec2
|
|
|
|
|
igCalcTextSize(addr charWidth, "A", nil, false, -1.0f)
|
|
|
|
|
let promptWidth = charWidth.x * float(promptIndicator.len())
|
|
|
|
|
let spacing = igGetStyle().ItemSpacing.x
|
|
|
|
|
|
|
|
|
|
igTextColored(vec4(1.0f, 1.0f, 1.0f, 1.0f), promptIndicator)
|
|
|
|
|
igSameLine(0.0f, spacing)
|
2025-09-11 18:18:13 +02:00
|
|
|
|
2025-09-12 10:15:13 +02:00
|
|
|
var availableWidth: ImVec2
|
|
|
|
|
igGetContentRegionAvail(addr availableWidth)
|
|
|
|
|
|
|
|
|
|
igSetNextItemWidth(availableWidth.x)
|
2025-09-11 18:18:13 +02:00
|
|
|
let inputFlags = ImGuiInputTextFlags_EnterReturnsTrue.int32 or ImGuiInputTextFlags_EscapeClearsAll.int32 or ImGuiInputTextFlags_CallbackCompletion.int32 or ImGuiInputTextFlags_CallbackHistory.int32
|
|
|
|
|
if igInputText("##Input", component.inputBuffer, 256, inputFlags, nil, nil):
|
2025-09-12 10:15:13 +02:00
|
|
|
echo component.inputBuffer
|
2025-09-10 18:25:15 +02:00
|
|
|
|
2025-09-12 10:15:13 +02:00
|
|
|
#[
|
|
|
|
|
Session information (requires footerHeight to be doubled)
|
|
|
|
|
]#
|
|
|
|
|
# igSeparator()
|
|
|
|
|
# igText(fmt"{component.agent.username}@{component.agent.hostname} [{component.agent.ip}]")
|
|
|
|
|
|
2025-09-11 18:18:13 +02:00
|
|
|
igSetItemDefaultFocus()
|