CLUI
repository·master·Indexed 20 days ago
https://github.com/vladimirmarkelov/cluiA Command Line User Interface library for Go inspired by TurboVision. It provides a variety of terminal-based UI controls including windows, frames, buttons, edit fields, progress bars, and specialized dialogs like FilePicker and LoginDialog. The library features built-in theme support, automatic layout scaling, and a comprehensive set of global and control-specific hotkeys for window manipulation and navigation.
What's inside clui
- All text within CLUI controls can be colorized using a simple set of internal tags. These tags allow you to temporarily change the text color or the background color of specific segments of text within a single control.
Use the File Picker dialog
masterThe File Picker is used to select a single file or directory. The dialog provides a list of files/directories, an edit field for quick search or entering new names, and buttons for Open (to enter a directory), Select (to confirm selection), and Cancel (to abort).How widget positioning and scaling work
masterCLUI handles layout automatically based on terminal size and parent constraints. You do not need to set positions manually. Key concepts include:
- Minimal Sizing: You should set the minimal width and height for a widget. If you pass the constant
AutoSize, the library calculates minimal sizes automatically (e.g., aLabel's minimal width is based on its text length). - Scaling Coefficients: When creating a widget, you can set a scale coefficient to control how it reacts to terminal/window resizing:
- Autoscaling: The widget automatically resizes when the terminal or parent window changes size.
- Fixed: Setting the coefficient to
Fixedprevents the widget from resizing, forcing it to maintain its minimal size. This is useful for elements likeButton.
- Constraints: While
Windowelements can overlap each other, a widget cannot overlap another widget.
- Minimal Sizing: You should set the minimal width and height for a widget. If you pass the constant
Core concepts of CLUI controls and hierarchy
masterCLUI organizes its UI elements into four distinct categories that define how they behave and how they must be structured in your application:
- Top level controls: The
Windowis the only top-level control. It is a visual element that does not have a parent and cannot be a child of another control. Every application must have at least one visibleWindow. If the lastWindowis closed, the application terminates. Windows can be modal. - Widgets: Most controls fall into this category. A widget must be a child of a
Windowor another widget; it cannot exist without a parent. Widgets can act as both parents and children. - Invisible controls: These are logical helpers that do not render visually. For example,
RadioGroupis an invisible control used to manage the selection logic ofRadiobuttons. - Dialogs: These are ready-to-use modal
Windowimplementations for common user interactions, such asConfirmationDialogandSelectDialog.
- Top level controls: The
How CLUI layout management works
masterCLUI uses a simplified layout model where any control becomes a container (layout) if it has children. Unlike toolkits like Qt, CLUI does not have dedicated layout objects; instead, the container's direction is determined by its children.
Layouts are strictly linear: controls are arranged either from left to right or from top to bottom in the order they are added. Consequently, a container is always either one control high or one control wide.
Key constraints to remember:
- No Fixed/Grid Layouts: Only automatic horizontal and vertical layouts are supported.
- Minimal Size Calculation: A container's real minimal size is the maximum of its own minimal values and the total space required by its children (including gaps and paddings). You cannot force a container to be smaller than the space required by its children.
- Alignment Trick: Since there is no explicit alignment API, to align a control to the bottom or right side, add a frameless
Framewithscaleset to1, then add the target control withscaleset toFixed. This allows the frame to expand with the parent while the control maintains its size and sticks to the edge.
// Example of the alignment trick for bottom/right alignment // 1. Add a frameless Frame with scale 1 (to expand) // 2. Add the control with scale Fixed (to stay at edge) container.AddChild(new Frame(..., scale: 1)) container.GetLastChild().AddChild(new MyControl(..., scale: Fixed))Window interaction and hotkeys
masterWindows are the only controls in CLUI that can be manually moved or resized using a mouse or keyboard.
Keyboard Hotkeys
Hotkeys are executed as key sequences: press the first combination, release it, and then press the second key.
Action Hotkey Sequence Resize Window Ctrl+Sfollowed by anarrow keyMove Window Ctrl+Pfollowed by anarrow keyMaximize/Restore Ctrl+WthenCtrl+MHide/Move to Background Ctrl+WthenCtrl+H(moves window to bottom of stack and activates next window)Mouse and Visual Cues
- Borders: The currently active window is indicated by a double border, while inactive windows have a single border.
- Window Icons: Located at the bottom-right corner, these allow for mouse manipulation:
- Move to background
- Maximize/Restore
- Close (Note: Closing the last remaining window will terminate the application).
- Navigation: Windows capture the
TABkey to allow users to move to the next child control using the keyboard.
How widget scaling and layout works
masterThe library uses a scaling coefficient to distribute extra space (
Delta) when a container is resized.- Starting Size: Calculated as the maximum of the container's minimal size and the sum of its children's minimal sizes.
- Delta Calculation:
Delta = NewSize - StartingSize. - Total Scale: The sum of all children's scale coefficients (
Fixedchildren have a scale of 0). - Distribution: Each child (except the last one with scale > 0) increases by:
child.Scale * Delta / TotalScaleSize. The remainingDeltais assigned to the last child.
Example: If two children both have
scale: 1and the parent size increases by 3, the first child grows byfloor(1 * (3 / 2)) = 1, and the second child grows by the remainder:3 - 1 = 2.Install CLUI via Go
masterTo add CLUI to your Go project, use the
go getcommand to fetch the module from GitHub.go get -u github.com/VladimirMarkelov/cluiUse color tags in widget text
masterYou can colorize text in widgets like
Label,Frame, orListBoxitems using HTML-like tags:<Letter:ColorValue>.Tags:
b: Background colorc,t,f: Text/Foreground color (cis a general color tag)
Supported Colors:
black,white,green,yellow,blue,magenta,cyan,red, anddefault(uses the widget's theme color).Modifiers:
bold(orbright),underline(orunderlined), andreverse.Usage Examples:
<t:red bold>: Red, bold text.<t:underline+blue>: Blue, underlined text.<c:green>text<c:>: Resets text color to the widget's default after the word "text".<b:>or<c:>: Shortcuts for resetting background or foreground to default.
Colors are temporary and only apply until the end of the string or until another tag is encountered. You do not need to manually reset colors at the end of a string.
"The <c:green>green<c:> text" "<t:red bold>Warning!<c:>"Add the CLUI library to your project
masterTo use CLUI in your Go application, import the library. It is common practice to use an alias like
uito keep calls concise.import ( ui "github.com/VladimirMarkelov/clui" )Create a widget
masterTo create a widget, you must provide a parent container at creation time; otherwise, the widget will be invisible and won't receive messages.
Common arguments for widget creation functions include:
parent: The container the widget belongs to.minimalWidth/minimalHeight: Limits for widget sizes when the parent is resized. Use the constantAutoSizeto allow the widget to calculate its own size (e.g., aLabeluses its title length) or use library defaults (e.g.,Framedefaults to 5x3).title: Text displayed on the widget (not supported by all widgets).scale: Defines how fast the widget grows relative to its siblings when the parent is resized. Use the constantFixedto prevent resizing.
Note:
Framerequires an additional argumentframeWidthwhich can beBorderThickorBorderThin.// Conceptual example of a widget creation signature CreateWidget(parent, minimalWidth, minimalHeight, title, scale)Initialize and finalize the CLUI library
masterCLUI must be initialized before creating any controls.
ui.InitLibrary()sets up control and theme managers, initializes the underlyingtermboxlibrary, and prepares the main event loop.To prevent the terminal cursor from disappearing (which happens because the library turns off the cursor at start), you must call
ui.DeinitLibrary()to clean up the terminal before the application exits. Usingdeferis the recommended way to ensure finalization occurs even if the application logic completes or crashes.func main() { ui.InitLibrary() defer ui.DeinitLibrary() // ... your other code ... }