170 Commits

Author SHA1 Message Date
c8946673c3 Fix layoutorder on file entries spazzing out in non-testbed apps. 2025-07-08 14:33:35 -05:00
d0a246bffe Update file information display in FileDialog. 2025-07-08 13:31:50 -05:00
e084e1120f Trying to solve for arrow angles. 2025-06-28 00:36:53 -05:00
238a7934b3 Adding Anchors and Decorator Widget types. 2025-06-27 19:55:02 -05:00
558f17fc94 Make ScrollingRect key-binds only work when mouse is inside. (This is subject to change) 2025-06-27 15:30:11 -05:00
4acd91b078 ContextMenu now opens on mouse-release versus initial mouse-press. TODO: Make it an enum configuration. 2025-06-27 15:29:17 -05:00
944bd66b43 Refactor CommandLine to not naiively override Window::Open, Close() 2025-06-27 15:28:06 -05:00
d935608e05 Fixed Window close button simply setting the element to invisible and not properly calling Open()/Close() procedure. 2025-06-27 15:27:29 -05:00
3b973c2c45 Added input-history feature to TextInputForm. Control via up/down arrows. 2025-06-27 15:26:33 -05:00
ce0c69190a Testing more of the filesystem API 2025-06-26 21:45:35 -05:00
dca15511ea Other edits. 2025-06-26 06:03:35 -05:00
beb5c97c13 Add UDim::FromPixels and UDim::FromScale constructors 2025-06-26 06:03:27 -05:00
505c2c70e6 Fixed Separator class spacing behavior 2025-06-26 06:03:04 -05:00
9b07a8ea01 Test of FileDialog.hpp 2025-06-26 06:02:43 -05:00
393ad1b2b3 Somewhat Fixed Checkbox drawing. 2025-06-26 06:02:30 -05:00
67416540d4 Typo 2025-06-26 05:36:27 -05:00
d9d92c2a28 Adjusting demo app to take screenshots. 2025-06-25 17:40:56 -05:00
086a3f226b Writing documentation in widgets.md, will deploy doxygen API reference tomorrow. 2025-06-25 02:46:48 -05:00
a2088b086b Add Tooltip::PopupDelay 2025-06-24 15:08:35 -05:00
86fb0cb2e6 Implementing hack for tooltip. 2025-06-24 13:46:33 -05:00
3a0743e787 Add FpsGraphWindow::DockGraph and UndockGraph 2025-06-24 02:52:48 -05:00
f46bf097ac Documentate Slider 2025-06-24 02:51:49 -05:00
b80fa9cd94 Add Window::HideTitlebar and ShowTitlebar 2025-06-24 02:51:20 -05:00
f229698971 Enhanced demo program. 2025-06-24 01:58:02 -05:00
96239db592 Redesign ColorPicker widget with the new slider capabilities in mind. 2025-06-24 01:57:53 -05:00
9d901c6300 Implement static draw routines to RectBase to unify JUI's rectangle motifs. 2025-06-24 01:57:38 -05:00
d5d6703ee9 Redesigned slider to support customizing the size of the Scrubber. 2025-06-24 01:57:14 -05:00
72e21451a6 Spastic Edits 2025-06-23 22:47:27 -05:00
8af6030d1a Refactored Slider to use an internal range of 0-1 and map it to a set range for the end-user. 2025-06-23 21:05:40 -05:00
0e62fcd5f6 Developing documentation. 2025-06-22 22:01:11 -05:00
938be6f7ca A ton of edits.. I don't even know 2025-06-16 12:02:10 -05:00
1c523dafa2 More supplemental docs. 2025-06-16 01:58:45 -05:00
80682e5fee Test of links to supplemental documentation. 2025-06-16 01:35:54 -05:00
fa5b9e23cf Documenting Link Widget. 2025-06-15 23:50:07 -05:00
3a0901693e Add LabeledSlider class. 2025-06-13 13:57:28 -05:00
4415e3c6b4 Test of Link Widget, brings me back around to several old issues that still need resolved. 2025-06-12 03:45:37 -05:00
1bd581a5d8 FPSGraphWindow 2.0 2025-06-09 20:48:12 -05:00
31d5074ed0 FPSGraphWindow 2025-06-09 20:16:05 -05:00
ab11857cfa Working on FPSGraph, built with temp-fixed JGL. 2025-06-09 16:37:27 -05:00
37d14cc0e0 Uhhhhhhhhh 2025-06-07 11:45:46 -05:00
134fa46f5a Update packages, having issue with Text-width with new packages, not quite sure. 2025-06-06 14:35:37 -05:00
5f53938f67 Update packages. 2025-06-04 14:34:38 -05:00
48b56213d8 Implemented a quick and dirty FPS graph, other small refactorings. 2025-06-04 04:52:19 -05:00
7ac0436a1f Performance optimization & bugfix
Only depth sort if we need to.
Fixed a bug where if you left the highest ancestor of a slider and re-entered it would still be sliding.
2025-05-30 22:22:09 -04:00
c4ada58e0c Fix sometimes-missing include of Slider in ColorPicker.hpp 2025-05-22 17:26:48 -05:00
21fee89e77 Refactoring for organization. 2025-05-13 14:28:02 -05:00
0ff787fd95 Refactor TextBase to TextStyler mixin. 2025-05-12 17:10:12 -05:00
75f30b062a Internal change of function signature. 2025-05-12 17:09:37 -05:00
3033edb903 Internal change of function signature. 2025-05-12 17:09:31 -05:00
814a7a8a3c Update to latest jlog release. 2025-05-10 13:07:08 -05:00
c538937c07 Expand Documentation for class Pinnable. 2025-05-02 02:31:34 -05:00
c183cae173 Based edits 2025-05-02 01:18:48 -05:00
bd8e9222de QOL edits. 2025-05-01 23:58:02 -05:00
672429f736 Iteration on functionality of ContextMenu and UtilityBar. Multi-nested menus are now supported. 2025-05-01 13:26:59 -05:00
abe4371f00 Implement Orientation and LineFillMode enums to the JUI namespace. 2025-05-01 13:26:27 -05:00
360171b46f NestableMenu header, will contain the NestableMenu mixin, once circular include issues are resolved. Will likely go with JUI/Forward.hpp to forward-declare all widget classes. 2025-05-01 13:26:10 -05:00
d8c48a338d Implement Separator widget class. Needs documentation and testing. 2025-05-01 13:25:34 -05:00
faf312206e JUI::Experimental namespace, currently empty. 2025-05-01 13:25:15 -05:00
1080a886cb Add TextStyleInterface.hpp -> Will be inherited by all text-drawing widgets, to apply style cohesively. Other StyleInterface classes are being devised. 2025-05-01 13:25:01 -05:00
b01b975ce9 Add triple-nested context menus to main demo, and some other minor fixes. 2025-05-01 00:38:49 -05:00
5ab0d89793 Integrate ContextMenu with Pinnable logic. 2025-05-01 00:38:14 -05:00
d807faea58 Implement Pinnable mixin class: Currently works something like a debounce for ContextMenus, but will be extended to lock widgets in-place. 2025-05-01 00:12:41 -05:00
eab4165417 Implement Automagic-adjust width of ContextMenu (Makes guesstimation due to problem with font-initialization at program startup 2025-04-30 02:44:23 -05:00
cf8c86c7c7 Remove TODO 2025-04-30 02:43:31 -05:00
b0c3203f70 Implement Widget::IsMouseInsideChildren and ::IsMouseInsideDescendants 2025-04-30 02:42:04 -05:00
2fed2ea760 Working on ContextMenu & UtilityBar 2025-04-29 15:44:01 -05:00
06f27b2436 Partial fix, UtilityBar submenu works, but the buttons within the Submenu still segfault. 2025-04-29 13:42:07 -05:00
543b1f81a4 Small Edits trying to fix Sigsegv 2025-04-29 13:13:04 -05:00
6f7388a27d Spitball Redesigns 2025-04-28 21:32:32 -05:00
c3e372ac29 Update mcolor dependency 2025-04-22 17:23:13 -04:00
fee9f2083c Write TabPageView header 2025-04-18 15:30:15 -04:00
fdaf68b01e Add TabView header 2025-04-18 00:40:52 -04:00
62cfd1bd58 Adjustments to Window and ScrollingRect widget. 2025-04-17 14:13:32 -04:00
b7ab9a8780 Shit 2025-04-12 12:46:38 -05:00
8d90f990f0 Fix ScrollingRect behavior with arrow keys, and add PageUp and PageDown key support. 2025-04-10 13:25:11 -05:00
5ccbb84e55 Add Window::Open, Close, SetOpen, IsOpen, and Toggle 2025-04-10 13:38:16 -04:00
74ae05321b add ObserveMouseInput override 2025-04-08 15:27:25 -04:00
906bde163a Other small fixes. 2025-04-07 00:09:36 -04:00
d0fcf9cce2 Refactor tweening some. 2025-04-07 00:09:17 -04:00
2e0ce74753 Implement debounce for collapsible. 2025-04-07 00:09:05 -04:00
47a7b19648 Continued. 2025-04-05 14:51:23 -04:00
80c28921b1 Refactored ScrollingRect to properly clamp the scrollbar size and position. 2025-04-05 14:51:13 -04:00
6166984daa Add Window::DragTo() 2025-04-05 14:49:28 -04:00
8589431cc7 Moving functions to cpp file 2025-04-05 14:49:06 -04:00
1092ef2b38 Add VerticalListLayout::CurrentContentHeight() and HorizontalListLayout::CurrentContentWidth() 2025-04-05 14:48:27 -04:00
1a11fa824b Implement window focusing. 2025-04-04 14:58:00 -04:00
1bd11c5235 Implement CommandLine Widget 2025-04-04 13:54:29 -04:00
956183bade Writing Logger 2025-04-03 23:36:10 -04:00
86d01099e2 Additions 2025-04-03 13:34:51 -04:00
90272e35f4 Editing demo program to have context menu that opens the console. 2025-04-02 15:26:23 -05:00
e2528f54fb Add BindMenu widget header 2025-04-02 15:25:59 -05:00
fffcc7f12d Add CommandLine window widget. 2025-04-02 15:25:41 -05:00
b3f65b3396 Adding new widgets 2025-04-02 13:19:06 -04:00
4193a2a693 Implementing input consumption refactor. 2025-04-01 18:56:43 -04:00
ee0e92d826 Specify default state of 'Dragging' for Slider. Add Slider::Dragging and Slider::SetDragging 2025-03-19 01:26:25 -04:00
86aa41a3db Added Widget::ObserveMouseWheel virtual method 2025-03-08 16:02:47 -05:00
3486ee03d7 yes 2025-03-02 05:24:51 -05:00
34257eb256 Merge remote-tracking branch 'origin/master' 2025-02-24 14:33:56 -05:00
c4f88c9021 Fix incorrect draw color on TextInputForm rendering the input buffer 2025-02-24 14:33:49 -05:00
052157f484 Windows check 2025-02-19 20:38:04 -06:00
bd02c91c45 Lit & Based 2025-02-19 21:21:18 -05:00
73a461ab56 Add widget counter 2025-02-19 16:54:44 -06:00
efb89dbcee Implement more Style 2025-02-19 16:54:36 -06:00
e607db2ae2 Edits 2025-02-19 16:54:22 -06:00
c7d214c851 Widget::ComputeElementPadding 2025-02-19 16:54:12 -06:00
116c2e2e2f etc 2025-02-18 23:13:50 -05:00
73ef647156 TextInputForm cleanup 2025-02-18 16:21:02 -06:00
bdbfe61b87 TweenAnchorPoint stub 2025-02-18 14:48:39 -05:00
f105053fb2 Edits 2025-02-17 22:35:08 -05:00
f772369686 Refactoring, bug fixes, and implementing styling data. 2025-02-14 15:29:00 -05:00
9cf78742ae Add TextInputForm::ClearTextOnReturn and ::DropFocusOnReturn 2025-02-13 22:41:33 -05:00
3aa9f419a2 Add Scene::GlobalUIScale 2025-02-13 22:41:16 -05:00
c338cc66a4 Update to latest JGL and mcolor 2025-02-11 19:29:51 -05:00
64ee666a83 Update main.cpp demo 2025-02-11 18:35:44 -05:00
13cbaa733c Added ImageButton::HoveredImageColor and other state variants 2025-02-11 18:35:31 -05:00
ad2e3e9a45 Added ImageRect::FitImageToBounds 2025-02-11 18:35:10 -05:00
24d5065f01 Changes to RectBase 2025-02-11 18:34:53 -05:00
bfd898b4f6 Change Window default style, implement Window::TitlebarHeight 2025-02-11 18:33:30 -05:00
2579b587c4 Added ImageBase::Draw overload that takes size of parent object to compute scale for image. 2025-02-11 18:32:49 -05:00
883eabb5e3 Awesome collaborative changes. 2025-02-10 21:07:31 -05:00
24e38b9ec3 Implement Widget tweening and layout order. 2025-02-10 16:42:33 -05:00
1b2fdecc18 Implement sorting of children for ListLayouts. 2025-02-10 16:42:14 -05:00
6e58e30022 Fully implement Tween class 2025-02-10 16:41:56 -05:00
030da85166 Implement UDim::Equals and UDim::Lerp 2025-02-10 16:41:44 -05:00
bd19b29a27 Implement UDim2::Equals and UDim2::Lerp 2025-02-10 16:41:39 -05:00
251daa4070 Work-In-Progress tween code. 2025-02-10 14:45:59 -05:00
fc65e9d229 ImageButton & change window exit to red circle. 2025-02-08 16:04:37 -05:00
2aaba5c1f7 Implement Tween class interface. 2025-02-08 15:35:01 -05:00
97edc67fad Tweening animation test 1. 2025-02-08 14:44:43 -05:00
1053a32317 Renamed several functions, testing tween support. 2025-02-08 04:07:00 -05:00
9a70839d2e Add JUI::Window:AbsoluteViewportPosition and AbsoluteViewportSize 2025-02-08 03:24:39 -05:00
578d15200c Add JUI::Window:LayoutControls Left & Right 2025-02-07 04:09:51 -05:00
15a2bfe667 Merge pull request 'test-jgl-changes' (#57) from test-jgl-changes into master
Reviewed-on: #57
2025-02-07 03:48:34 -05:00
d204d991c1 More Scroller Testing 2025-02-07 03:47:44 -05:00
29f5125ccc Test MSAA 2025-02-07 02:42:30 -05:00
c44e5c830c Logger Window Test!! 2025-02-07 02:40:13 -05:00
c78f9a045c JGL::RenderTarget::Resize appears to have an issue. 2025-02-07 01:56:25 -05:00
7f2477677a Refactoring entire render logic to support ScrollingRect, got some results, but still having issues. 2025-02-07 01:48:22 -05:00
fe454209f7 Other small edits 2025-02-04 19:59:03 -05:00
5673ce3b9e Changes to WIP ScrollingRect 2025-02-04 19:53:43 -05:00
02cd200114 Changes to WIP ScrollingRect 2025-02-04 19:53:40 -05:00
32d9615001 Change behavior of tooltip and UtilityBar 2025-02-04 19:51:42 -05:00
5efbe3fea3 Migrate to latest JGL 2025-02-04 19:51:19 -05:00
1520db8dcd Merge remote-tracking branch 'origin/master' 2025-02-04 19:50:59 -05:00
663355726b Refactor RectBase to allow blit to RenderTarget 2025-02-04 19:43:57 -05:00
83ffecb91d Hoverable class no longer drives tooltips. Rather, a tooltip widget can be parented to any widget. 2025-02-04 19:43:36 -05:00
135a1564f2 Remove Canvas class 2025-02-04 19:41:21 -05:00
d3f29e2072 Fix update chain in Button class 2025-02-04 19:41:11 -05:00
5f7a2326bd CHange showcase icon. 2025-02-04 16:19:33 -06:00
8acf32f140 Update README.md 2025-02-04 17:09:18 -05:00
d89d74052c Update README.md 2025-02-04 17:08:49 -05:00
2e65380ae6 Update README.md 2025-02-04 17:07:45 -05:00
5bf6e2cabc Add 9slice sample image. 2025-02-03 00:15:15 -05:00
593ba22626 Implemented NineSliceRect widget. 2025-02-03 00:13:56 -05:00
27cde1f37c Implemented NineSliceRect widget. 2025-02-02 23:59:15 -05:00
103813e9c1 Fix bug in slider. 2025-01-28 18:59:20 -05:00
0ef7ec2a7f Migrate to latest ReWindow and JGL 2025-01-28 18:35:34 -05:00
0cd3fbfc17 Bugfixes for Slider and Window widget. 2025-01-28 18:11:35 -05:00
e7e469864f Cleanup & Stater
Make it so when you modify any element of the text that would necessitate redrawing the text we do.
2025-01-21 11:07:22 -05:00
0f00cfa48d Merge branch 'render_targets_for_text' 2025-01-20 20:49:42 -05:00
c9a9c6aeb5 Fixed.
Update to latest JGL & check if bounds is zero because that breaks *everything* to do with RenderTargets.
2025-01-20 20:49:21 -05:00
355e9f975e Edits to ScrollingRect. 2025-01-19 13:54:20 -05:00
5293a845ad ???? 2025-01-19 01:58:59 -05:00
a31ee731d2 Merge remote-tracking branch 'origin/master' 2025-01-16 00:07:26 -05:00
389f266722 Fixed some functions, will check into the rest in the morning. 2025-01-16 00:07:07 -05:00
Redacted
e32bce6b50 Update CMakeLists.txt 2025-01-15 23:02:08 -05:00
93632faaed Text Input Form Autocomplete text. 2025-01-09 13:19:20 -05:00
e11b8c8c50 Text Input Form OnReturn event. 2025-01-08 15:35:14 -05:00
2e0d75391e Text Input Form proper cursor behavior. 2025-01-08 13:53:11 -05:00
bc8ba3ed02 Implementing Text Input Form and it's features. 2025-01-07 19:09:37 -05:00
110 changed files with 9609 additions and 1547 deletions

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.18..3.27)
project(JUI
VERSION 1.1
VERSION 4.0
LANGUAGES CXX)
if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
@@ -26,34 +26,44 @@ if (UNIX)
endif()
if (WIN32)
add_library(JUI STATIC ${JUI_SRC})
add_library(JUI STATIC ${JUI_SRC}
include/JUI/Mixins/DragAndDropReceiver.hpp
include/JUI/Widgets/RadioButtonSet.hpp
src/JUI/Widgets/UtilityBar.cpp
include/JUI/Widgets/ContextMenu.hpp)
endif()
set_target_properties(JUI PROPERTIES LINKER_LANGUAGE CXX)
CPMAddPackage(
NAME mcolor
URL https://git.redacted.cc/maxine/mcolor/archive/Release-1.zip
)
CPMAddPackage(
NAME Event
URL https://git.redacted.cc/josh/Event/archive/Release-12.zip
)
CPMAddPackage(
NAME J3ML
URL https://git.redacted.cc/josh/j3ml/archive/3.4.3.zip
NAME jlog
URL https://git.redacted.cc/josh/jlog/archive/Prerelease-19.zip
)
CPMAddPackage(
NAME jlog
URL https://git.redacted.cc/josh/jlog/archive/Prerelease-16.zip
NAME J3ML
URL https://git.redacted.cc/josh/j3ml/archive/3.4.5.zip
)
CPMAddPackage(
NAME ReWindow
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-26.zip
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-34.zip
)
CPMAddPackage(
NAME JGL
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-42.zip
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-58.zip
)
target_include_directories(JUI PUBLIC ${Event_SOURCE_DIR}/include)
@@ -65,7 +75,7 @@ target_include_directories(JUI PUBLIC ${ReWindow_SOURCE_DIR}/include)
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})
install(FILES ${JUI_HEADERS} DESTINATION include/${PROJECT_NAME})
target_link_libraries(JUI PUBLIC Event J3ML jlog ReWindowLibrary JGL)
target_link_libraries(JUI PUBLIC Event J3ML jlog ReWindow JGL)
add_executable(RedactedJUIDemo main.cpp)
target_link_libraries(RedactedJUIDemo PUBLIC JUI)

2782
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,52 +2,53 @@
![Static Badge](https://img.shields.io/badge/Lit-Based-%20)
Fourth Time's The Charm! (tm)
Fourth Time's The Charm!
JUI is a C++20 Library for building interactive menus in OpenGL / Redacted3D.
#### JUI is a C++20 Library for building interactive menus in OpenGL / Redacted3D.
It is expressly built with our Redacted3D engine in mind, but steps have been taken to support OpenGL generally.
![JUI First Showcase](showcase.png)
## Abstract
JUI provides a set of objects that we term Widgets. Widgets can be styled and laid out on-screen in relation to each other. Each widget has a single parent, and a list of child elements. Your root widget should be a Scene object.
Provided widgets include Scene, Rect, Text, TextRect, Button, TextButton, TextInputForms, Slider, Image, ImageRect, RadioButton
JUI provides a set of objects that we term Widgets, which can be styled and laid out on-screen in relation to each other. Each widget has a single parent, and a list of child elements. Your root widget should be a Scene object.
## Features
### Why use this instead of imgui?
## Usage
* Flexible and comprehensive set of Widgets. [Full List Here](docs/widgets.md)
* Extendable - Widgets can be extended via class derivation, and even combined to create complex behavior.
* Low-overhead stateful GUI elements.
* Easy integration with your project. Simply provide update, draw, and user-input callbacks to your scene.
## Examples
![JUI First Showcase](showcase.png)
Browse the src/Demos directories for examples of building and interacting with things in JUI.
Browse the Repository's ![Wiki](https://git.redacted.cc/josh/ReJUI/wiki), linked above, for further explainations and sample code on each widget.
## Dependencies
ReJUI shares dependencies with it's rendering layer, ![JGL](https://git.redacted.cc/josh/JGL).
Currently, the package is also integrated with ![J3ML](https://git.redacted.cc/josh/J3ML) and ![ReWindow](https://git.redacted.cc/Redacted/ReWindow).
`Fedora/RHEL: dnf install cmake make gcc-g++ libX11 libX11-devel mesa-libGL-devel vulkan-loader-devel`
`Ubuntu/Debian: apt-get install cmake make gcc g++ libx11-6 libx11-dev libgl-dev libvulkan-dev libxrandr-dev`
## Documentation
Documentation is automatically generated from latest commit and is hosted at https://doc.redacted.cc/jui .
[An overview of the library is available here.](docs/overview.md)
[A full list of widgets is available here.](docs/widgets.md)
## Contributing
Contributions to JUI are welcome! Feel free to file bug reports or feature requests by creating an Issue. Pull requests are also very welcome!
## History
JUI started out as my menu toolkit for the LOVE2D framework many years ago. Between then and now I had re-implemented it twice, once for MonoGame Framework, and again in C++ for SDL2. Legacy versions are listed below.
JUI v1 - LOVE2D / Lua
JUI v2 - MonoGame / C#
JUI v3 - SDL2 / C++
## License
JUI is expressly released without a license, under no restrictions. We dedicate all of our works to the public domain for the (hopeful) betterment of humanity.

BIN
assets/9slice.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

0
docs/animation.md Normal file
View File

0
docs/layout.md Normal file
View File

29
docs/overview.md Normal file
View File

@@ -0,0 +1,29 @@
<details>
<summary>Data Types</summary>
<br/>
<details>
<summary>UDim - Universal Dimension</summary>
This is some information about the Widget class.
</details>
<br/>
<details>
<summary>UDim2 Universal Dimension 2D </summary>
This is some information about the Widget class.
</details>
<br/>
<details>
<summary>Event Event Signalling API</summary>
This is some information about the Widget class.
</details>
<br/>
<details>
<summary>mcolor::Color3, mcolor::Color4 - Color Representation API </summary>
This is some information about the Widget class.
</details>
<br/>
</details>
<details><summary>Widgets</summary>
This is some information about the Widget class.
</details>

0
docs/styling.md Normal file
View File

300
docs/widgets.md Normal file
View File

@@ -0,0 +1,300 @@
## JUI Widget Directory
Updated on 2025-06-25 / Release-6.4 [^1]
See also:
[Animation](animation.md) <br/>
[Layout](layout.md) <br/>
[Overview](overview.md) <br/>
[Styling](styling.md) <br/>
[Full API Reference](https://doc.redacted.cc/jui) <br/>
[Repository](https://git.redacted.cc/josh/ReJUI)
<details><summary>Widget</summary>
* The [Widget](doc.redacted.cc/jui/) is the basis of all GUI elements in JUI.
* It is an abstract class, and you should not directly instantiate `Widget` for use in your UI.
* Instead, create its concrete derived types (`Button`, `Text`, `Slider`) or your own custom derivation.
* `class Widget` serves as the starting point for creating custom widgets.
* Widgets are organized in a tree-like hierarchy, where each widget can have multiple **children** and a single **parent**.
* This structure dictates rendering order, event propagation, and coordinate systems.
* Operations like updating, drawing, and event dispatching typically traverse this hierarchy **recursively**.
* A Widget's on-screen position is always relative to its parent. This simplifies layout management, as moving a parent automatically moves all its children. Sizing is also handled in relation to the parent.
* Widgets can be given a custom string identifier which can be used to search for it in the hierarchy later.
* ```Widget::Name("unique_element_4");```
```cpp
// Dispatch a "MouseMovement" event until a widget indicates that it has "observed" the event.
for (auto* widget : my_element->GetChildren()) {
if (widget->ObserveMouseMovement({420, 420}) return;
}
// Listens for an event of a child widget being added.
// These event-hooks are provided to make customization easier without requiring you to derive the class.
my_element->ChildAdded += [] (Widget* child) {
child->Name("New Child Element!");
};
```
</details>
<details><summary>Scene</summary>
* The [Scene]() widget acts as the **root container** for all your JUI elements within a window or application.
* It's the top-level `Widget` that manages the entire hierarchy of your UI.
* You'll typically have one `Scene` per window, serving as the canvas upon which all other widgets are placed and rendered.
* Every JUI application or window requires a `Scene` to host its user interface.
* All other widgets become descendants of the `Scene`.
* A scene is a **mandatory** component for a JUI application. You cannot display widgets without placing them in a `Scene`.
* Unlike other `Widget` types, a `Scene` does not have a parent itself. It's the **root** of the hierarchy.
* Event Sink - Events from the OS / Framework (mouse clicks, key presses) are first received by the `Scene`, which then initiates the recursive dispatch process down its tree.
* Z-Ordering: While individual widgets use Z-Index for sibling ordering, the `Scene` ensures that its entire content is rendered correctly, typically on top of the OpenGL scene if applicable.
* A scene's viewport (aka canvas size) can be set to the window viewport, or an arbitrary value.
* The scene provides a GlobalUIScale property, that can be used to scale an entire GUI uniformly.
```cpp
auto* scene = new JUI::Scene();
auto* text = new JUI::Text(scene);
text->Content("Hello, World!");
void app_key_press(Key key) {
// Returns whether any widget should have "consumed" the event.
bool sunk = scene->ObserveKeyInput(key, true);
}
void app_key_release(Key key) {
// Returns whether any widget should have "consumed" the event.
bool sunk = scene->ObserveKeyInput(key, false);
}
```
</details>
<details>
<summary>BindMenu</summary>
### The BindMenu Widget is a work-in-progress. Check back in Prerelease 7.
</details>
<details>
<summary>Button</summary>
This is some information about the Widget class.
</details>
<details><summary>Checkbox</summary>
* The `Checkbox` widget provides a simple binary toggle input, allowing users to select or deselect an option.
* Typically Consists of a small square box.
* When clicked, the box's state flips, indicating whether the corresponding option is enabled or disabled.
* Also see LabeledCheckbox for a Checkbox with integrated text.
* Usages:
* Enabling / Disabling Features
* Opt-in / Opt-out Choices:
* Multiple Selections - When presented in a group, checkboxes allow users to select zero, one or multiple independent options simultaneously.
* Contrast with Radio Buttons, which allow only one selection from a group.
* Boolean Settings
</details>
<details><summary>Collapsible</summary>
* The `Collapsible` widget provides a way to **show or hide** a section of UI content with a single click, typically on a header or title bar.
* It's a space-saving component that allows users to expand details when needed and collapse them to reduce visual clutter.
* It's ideal for settings panels, property inspectors, or long lists of categorized information.
* Usages:
* Settings Panel - Group related settings that can be expanded individually.
* Accordions - Create a series of Collapsibles where only one can be open at a time, by combining them with custom logic on the open/close events.
* Property Inspectors - Display object properties that can be shown or hidden.
* Information Hiding - Presenting advanced options or less frequently used details that don't need to be visible by default.
* Debug Overlays - Containing logs or diagnostic tools that can be toggled open.
* Notes
* Content Container
* Toggle Mechanism
* Layout Impact
* Initial State
</details>
<details><summary>ColorPicker</summary>
This is some information about the Widget class.
</details>
<details><summary>ComboBox</summary>
This is some information about the Widget class.
</details>
<details><summary>CommandLine</summary>
This is some information about the Widget class.
</details>
<details><summary>ContextMenu</summary>
This is some information about the Widget class.
</details>
<details><summary>DialogWindow</summary>
This is some information about the Widget class.
</details>
<details><summary>FileDialog</summary>
This is some information about the Widget class.
</details>
<details><summary>FpsGraph</summary>
This is some information about the Widget class.
</details>
<details><summary>Graph</summary>
This is some information about the Widget class.
</details>
<details><summary>GridLayout</summary>
This is some information about the Widget class.
</details>
<details><summary>Image</summary>
This is some information about the Widget class.
</details>
<details><summary>ImageButton</summary>
This is some information about the Widget class.
</details>
<details><summary>ImageRect</summary>
This is some information about the Widget class.
</details>
<details><summary>LabeledCheckbox</summary>
This is some information about the Widget class.
</details>
<details><summary>LabeledSlider</summary>
This is some information about the Widget class.
</details>
<details><summary>Link</summary>
This is some information about the Widget class.
</details>
<details><summary>ListBox</summary>
This is some information about the Widget class.
</details>
<details><summary>VerticalListLayout</summary>
This is some information about the Widget class.
</details>
<details><summary>HorizontalListLayout</summary>
This is some information about the Widget class.
</details>
<details><summary>NineSlice</summary>
This is some information about the Widget class.
</details>
<details><summary>ProgressBar</summary>
This is some information about the Widget class.
</details>
<details><summary>RadioButton</summary>
This is some information about the Widget class.
</details>
<details><summary>Rect</summary>
This is some information about the Widget class.
</details>
<details><summary>RichText</summary>
This is some information about the Widget class.
</details>
<details><summary>ScrollingRect</summary>
This is some information about the Widget class.
</details>
<details><summary>Separator</summary>
This is some information about the Widget class.
</details>
<details><summary>Slider</summary>
* The `Slider` Widget allows users to select a value from a continuous or discrete range by moving a **scrubber** (or thumb, dragger, etc.) along a **track**.
* `Slider` is a specialization of the `Rect` widget.
* Usages:
* Volume Control
* Brightess/Contrast
* Color Picker (JUI actually has [one](ColorPicker) built-in that uses `Sliders`!)
* Progress Indicator (Just kidding, use a `ProgressBar` for this!);
* Game Settings
* Field-of-view.
* Sensitivity
* Spell power, physics parameters.
* Numerical Input (Range-Bound): An alternative to a text input field when the allowed values are within a known, limited range.
* Notes:
* Orientation: ===Currently, JUI does not **yet** have vertical sliders.===
* Range and Step: You can define a minimum and maximum value for the slider;
* As well as a "step" quantity, forcing the slider to snap to discrete increments (ie. multiples of 5, or increments of 0.25)
* Event-Driven - The "value changed" event fires when the user moves the scrubber or the value is set programmatically.
```cpp
auto* sfx_volume_slider = new JUI::Slider(my_scene);
sfx_volume_slider->Minimum(0);
sfx_volume_slider->Maximum(100);
sfx_volume_slider->Interval(1);
sfx_volume_slider->CurrentValue(50);
sfx_volume_slider->ValueChanged += [](float value) {
// set audio engine SFX volume.
};
```
* Also see `LabeledSlider` for a slider with text.
</details>
<details><summary>Table</summary>
This is some information about the Widget class.
</details>
<details><summary>TabView</summary>
This is some information about the Widget class.
</details>
<details><summary>Text</summary>
This is some information about the Widget class.
</details>
<details><summary>TextButton</summary>
This is some information about the Widget class.
</details>
<details><summary>TextInputForm</summary>
This is some information about the Widget class.
</details>
<details><summary>TextRect</summary>
This is some information about the Widget class.
</details>
<details><summary>ToggleButton</summary>
This is some information about the Widget class.
</details>
<details><summary>Tooltip</summary>
This is some information about the Widget class.
</details>
<details><summary>UtilityBar</summary>
This is some information about the Widget class.
</details>
<details><summary>Viewport</summary>
This is some information about the Widget class.
</details>
<details><summary>Window</summary>
This is some information about the Widget class.
</details>

View File

@@ -0,0 +1,12 @@
#pragma region
namespace JUI {
/// The CanvasBase class is an object that handles managing and drawing a RenderTarget reference.
/// This class is similar to ImageBase in that it contains and renders a texture-type object,
/// But differs in that this one is intended to be used for interactive drawing.
class CanvasBase {
};
}

View File

@@ -14,23 +14,29 @@
#include <Color4.hpp>
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <JGL/types/Texture.h>
#include <JGL/types/RenderTarget.h>
using J3ML::LinearAlgebra::Vector2;
namespace JUI
{
namespace JUI {
/// The ImageBase class is an object that handles managing and rendering a texture reference.
/// This class is used as a mixin on widgets that must render images. i.e. Image class
/// This class is used as a mixin on widgets that must render images. i.e. Image class.
/// This object is complex, stateful, and manages resources. Do not use this as a general-purpose texture data type.
/// @see JGL::Texture and JGL::RenderTarget for more suitable classes.
class ImageBase
{
public:
/// The default constructor initializes this ImageBase with a null pointer texture.
ImageBase();
ImageBase(JGL::RenderTarget *content);
/// This constructor initializes this ImageBase with a JGL::Texture pointer.
explicit ImageBase(JGL::Texture* texture);
public:
[[nodiscard]] JGL::RenderTarget* RenderTarget() const { return texture;}
/// Returns the texture pointer held by this object.
[[nodiscard]] JGL::Texture* Content() const;
[[nodiscard]] const JGL::Texture* Content() const;
/// Returns the color of this object. The texture is rendered with this color.
[[nodiscard]] Color4 Color() const;
/// Returns the scale factor of this object. The texture is scaled with this value.
@@ -39,16 +45,20 @@ namespace JUI
[[nodiscard]] Vector2 Origin() const;
/// Sets the texture of this object.
void Content(JGL::Texture* content);
void Content(const JGL::Texture* content);
void Content(JGL::RenderTarget* content);
/// Sets the color of this object.
void Color(const Color4& newColor);
/// Sets the scale factor of this object.
void Scale(const Vector2& newScale);
void Origin(const Vector2& newOrigin);
public:
/// Draws the image at the given position, with the instances' scale and origin.
void Draw(const Vector2& pos);
/// Draws the image at the given pos, manually scaled to fit the given size.
void Draw(const Vector2& pos, const Vector2& size);
protected:
JGL::Texture* texture;
JGL::RenderTarget* texture;
Color4 image_color = Color4(255,255,255);
Vector2 scale = Vector2(1,1);
Vector2 origin = Vector2(0,0);

View File

@@ -15,10 +15,13 @@
#include <Event.h>
#include <Color4.hpp>
#include "JGL/types/RenderTarget.h"
#include <JUI/DefaultStyle.hpp>
#include "JGL/JGL.h"
namespace JUI
{
enum class BorderMode
{
Outline, /// As BorderWidth increases, the border grows outward. The dimensions of the widgets contents do not change.
@@ -26,6 +29,10 @@ namespace JUI
Inset /// As BorderWidth increases, the border grows inward only. The dimensions of the widget's contents are reduced at a 1:2 ratio.
};
enum class CornerRoundingMode {
None, Rounded, Chamfer,
};
/// Base implementation for rendering rectangles with decorations.
class RectBase {
public:
@@ -36,9 +43,10 @@ namespace JUI
//Event<Vector2> MousePress;
//Event<Vector2, bool> MouseRelease;
public:
void CornerRounding(float radius);
virtual void CornerRounding(float radius);
float CornerRounding() const;
[[nodiscard]] float CornerRounding() const;
enum CornerRoundingMode CornerRoundingMode() const;
// TODO: Implement per-corner rounding in JGL::Outline/FillRect
//void CornerRounding(float tlRadius, float trRadius, float blRadius, float brRadius);
@@ -50,13 +58,13 @@ namespace JUI
void SetClipsDescendants(bool clipping);
void BGColor(const Color4& col);
void BorderColor(const Color4& col);
void SetBorderWidth(float w);
void BorderWidth(float w);
Color4 BGColor() const;
Color4 GetBorderColor() const;
float GetBorderWidth() const;
enum BorderMode BorderMode() const;
[[nodiscard]] Color4 BGColor() const;
[[nodiscard]] Color4 GetBorderColor() const;
[[nodiscard]] float GetBorderWidth() const;
[[nodiscard]] enum BorderMode BorderMode() const;
void BorderMode(const enum BorderMode& mode);
@@ -67,14 +75,33 @@ namespace JUI
void Draw(const Vector2& pos, const Vector2& size);
void Draw(const Color4& bgColor, const Color4& fgColor, const Vector2& pos, const Vector2& size);
/// Core routine for drawing outline rects.
static void DrawOutline(const Color4& color,
const Vector2& pos, const Vector2& size,
float rounding, float borderWidth,
enum BorderMode borderMode, enum CornerRoundingMode cornerRoundingMode);
/// Core routine for drawing background rects.
static void DrawBG(const Color4& color,
const Vector2& pos, const Vector2& size,
float rounding,
enum CornerRoundingMode rounding_mode);
/// Core routine for drawing rect boxes, with background and outline.
static void Draw(const Color4& bgColor, const Color4& fgColor,
const Vector2& pos, const Vector2& size,
float rounding, float borderWidth,
enum BorderMode borderMode, enum CornerRoundingMode rounding_mode);
protected:
enum BorderMode border_mode = BorderMode::Middle;
bool mouse_press_debounce;
bool mouse_inside_debounce;
float border_width = 1.f;
Color4 bg_color = {128,128,128, 255};
Color4 border_color = {192, 192, 192, 0};
enum CornerRoundingMode corner_mode = CornerRoundingMode::Rounded;
bool mouse_press_debounce{};
bool mouse_inside_debounce{};
float border_width = Style::BorderLineWidth;
Color4 bg_color = Style::BackgroundColor;
Color4 border_color = Style::BorderColor;
bool clips_descendants = false; // Controls if child objects can render outside of their parent's rectangle bounds.
float corner_rounding_radius = 0.f; // Curves the rectangle corners by N degrees.
};

View File

@@ -16,20 +16,14 @@
#include <JGL/JGL.h>
#include <JGL/types/Font.h>
#include "../Style/TextStyler.hpp"
using J3ML::LinearAlgebra::Vector2;
namespace JUI
{
/// Enumerations for alignment of text within a desired "Box".
namespace TextAlign {
enum class H { Left, Center, Right }; // Horizontal
enum class V { Top, Center, Bottom }; // Vertical
}
namespace JUI {
class TextBase;
/// How should a text widget behave when it's text runs out of room?
enum class TextOverflowMode
{
enum class TextOverflowMode {
WRAP, /// Wraps at the nearest 'word', or otherwise appropriate stop in text.
WRAP_ANYWHERE, /// Wraps the text anywhere.
TRUNCATE, /// Cuts the text off and suffixes '...' to indicate the lack of the full intended message.
@@ -37,93 +31,122 @@ namespace JUI
TRUNCATE_NO_DOTS, /// Cuts the text off at the first appropriate break in text.
TRUNCATE_ANYWHERE_NO_DOTS /// Cuts the text off at any point in the string.
};
}
/// TextBase class, implements core mechanics of drawing text in JUI, and is used by Text Widget class.
class TextBase {
public:
TextBase();
/// Enumerations for alignment of text within a desired "Box".
namespace JUI::TextAlign {
// Horizontal
enum class H { Left, Center, Right };
// Vertical
enum class V { Top, Center, Bottom };
}
[[nodiscard]] std::string GetContent() const;
[[nodiscard]] Color4 GetTextColor() const;
[[nodiscard]] Color4 GetOutlineColor() const;
[[nodiscard]] TextAlign::H GetHorizontalTextAlign() const;
[[nodiscard]] TextAlign::V GetVerticalTextAlign() const;
/// TextBase class, implements core mechanics of drawing text in JUI, and is used by Text Widget class.
class JUI::TextBase : public TextStyler {
public:
#pragma region Constructors
~TextBase() { delete text_canvas; };
TextOverflowMode GetOverflowMode() const;
TextBase() : TextStyler(), text_canvas(new RenderTarget({1, 1}, {0,0,0,0})) {};
#pragma endregion
//TextBase() : set_font(JGL::Fonts::Jupiteroid), state_redraw(true), text_canvas(new RenderTarget({1, 1}, {0, 0, 0, 0})) {};
public:
#pragma region Getters
/// Returns the
[[nodiscard]] Vector2 GetTextBounds();
[[nodiscard]] Vector2 GetTextPosition() const;
[[nodiscard]] JGL::Font GetFont() const;
[[nodiscard]] u32 GetTextSize() const;
#pragma endregion
public:
#pragma region Setters
#pragma endregion
void Update(float delta);
// I don't know why this function even exists, or why it was public. It lets you circumvent
// the whole purpose of storing the state things are in :/ - Redacted.
void Draw(const Vector2& abs_pos, const Vector2& abs_size, const std::string& content, unsigned int size, const Color4& color);
void SetOverflowMode(const TextOverflowMode& mode);
/// Renders the aligned text string within a bounding-box specified by abs_pos (top-left corner), and abs_size.
/// @see Widget::Draw(), Text::Draw().
void Draw(const Vector2& abs_pos, const Vector2& abs_size);
private:
bool state_redraw = true;
protected:
RenderTarget* text_canvas;
std::string content = "Sample Text";
float text_outline = 1.f;
Color4 outline_color = {255,255,255};
bool word_wrap = false;
TextAlign::H h_align = TextAlign::H::Left;
TextAlign::V v_align = TextAlign::V::Top;
TextOverflowMode overflow_mode;
//Color4 text_color = {255,255,255};
//JGL::Font set_font = JGL::Fonts::Jupiteroid;
//u32 text_size = 12;
protected:
void SetTextSize(u32 size);
public:
[[nodiscard]] std::string Content() const;
[[nodiscard]] Color4 OutlineColor() const;
[[nodiscard]] TextAlign::H HorizontalTextAlign() const;
[[nodiscard]] TextAlign::V VerticalTextAlign() const;
TextOverflowMode OverflowMode() const;
[[nodiscard]] Vector2 TextBounds();
[[nodiscard]] Vector2 TextPosition() const;
public:
void SetFont(const JGL::Font& font);
void WordWrap(bool wrap);
void OverflowMode(const TextOverflowMode& mode);
void SetContent(const std::string& content);
void Content(const std::string& content);
void SetTextColor(const Color4& color);
/// @note This member is deprecated in favor of TextSize().
void TextSize(int size) override;
void SetOutlineColor(const Color4& color);
/// @note This member is deprecated in favor of Font().
void Font(const JGL::Font& font) override;
/// @note This member is deprecated in favor of TextColor().
void TextColor(const Color4& color) override;
/// Set the horizontal alignment of this text widget in relation to it's parent,
/// while keeping the text retained inside the parent widget's bounds.
/// @see TextAlign::H
void SetHorizontalTextAlign(const TextAlign::H& align);
/// Set the vertical alignment of this text widget in relation to it's parent,
/// while keeping the text retained inside the parent widget's bounds.
/// @see TextAlign::V
void SetVerticalTextAlign(const TextAlign::V& align);
void OutlineColor(const Color4& color);
/// Sets the horizontal and vertical alignment of this text widget.
/// @see TextAlign::H and TextAlign::V.
void SetTextAlign(const TextAlign::H& h_align, const TextAlign::V& v_align);
/// Set the horizontal alignment of this text widget in relation to it's parent,
/// while keeping the text retained inside the parent widget's bounds.
/// @see TextAlign::H
void HorizontalTextAlign(const TextAlign::H& align);
/// Aligns the text of this widget to the left-hand-side of the parent's bounding box.
/// @see SetHorizontalTextAlign, TextAlign::H
void AlignLeft();
/// Aligns the text of this widget to the right-hand-side of the parent's bounding box.
/// @see SetHorizontalTextAlign, TextAlign::H
void AlignRight();
/// Aligns the text of this widget to the top of the parent's bounding box.
/// @see SetVerticalTextAlign, TextAlign::V
void AlignTop();
/// Aligns the text of this widget to the bottom of the parent's bounding box.
/// @see SetVerticalTextAlign, TextAlign::V
void AlignBottom();
/// Centers the text of this widget in relation to the parent's bounding box.
/// @see SetHorizontalTextAlign, SetVerticalTextAlign, TextAlign enums
void AlignCenterBoth();
/// Alias for AlignCenterBoth().
void Center();
/// Aligns the text of this widget to the horizontal center of the parent's bounding box.
void AlignCenterHorizontally();
/// Aligns the text of this widget to the vertical center of the parent's bounding box.
void AlignCenterVertically();
/// Set the vertical alignment of this text widget in relation to it's parent,
/// while keeping the text retained inside the parent widget's bounds.
/// @see TextAlign::V
void VerticalTextAlign(const TextAlign::V& align);
void Update(float delta);
/// Sets the horizontal and vertical alignment of this text widget.
/// @see TextAlign::H and TextAlign::V.
void Align(const TextAlign::H& h_align, const TextAlign::V& v_align);
/// Renders the aligned text string within a bounding-box specified by abs_pos (top-left corner), and abs_size.
/// @see Widget::Draw(), Text::Draw().
void Draw(const Vector2& abs_pos, const Vector2& abs_size);
/// Aligns the text of this widget to the left-hand-side of the parent's bounding box.
/// @see SetHorizontalTextAlign, TextAlign::H
void AlignLeft();
void SetWordWrap(bool wrap);
/// Aligns the text of this widget to the right-hand-side of the parent's bounding box.
/// @see SetHorizontalTextAlign, TextAlign::H
void AlignRight();
protected:
std::string content = "Sample Text";
Color4 text_color = {255,255,255};
float text_outline = 1.f;
Color4 outline_color = {255,255,255};
bool word_wrap = false;
TextAlign::H h_align = TextAlign::H::Left;
TextAlign::V v_align = TextAlign::V::Top;
TextOverflowMode overflow_mode;
JGL::Font set_font = JGL::Fonts::Jupiteroid;
u32 text_size = 12;
/// Aligns the text of this widget to the top of the parent's bounding box.
/// @see SetVerticalTextAlign, TextAlign::V
void AlignTop();
};
/// Aligns the text of this widget to the bottom of the parent's bounding box.
/// @see SetVerticalTextAlign, TextAlign::V
void AlignBottom();
}
/// Centers the text of this widget in relation to the parent's bounding box.
/// @see SetHorizontalTextAlign, SetVerticalTextAlign, TextAlign enums
void AlignCenterBoth();
/// Alias for AlignCenterBoth().
void Center();
/// Aligns the text of this widget to the horizontal center of the parent's bounding box.
void AlignCenterHorizontally();
/// Aligns the text of this widget to the vertical center of the parent's bounding box.
void AlignCenterVertically();
};

View File

@@ -11,6 +11,16 @@
#pragma once
/// Ideal refactor:
/// * Completely separate Component for a given behavior / area of concern
/// * XYZStyler class, stored as a pointer in the widget.
/// * Smart pointers
/// * Intelligent layout.
/// *
#include <Event.h>
#include <string>
#include <vector>
@@ -19,66 +29,66 @@
#include <Color3.hpp>
#include <Color4.hpp>
#include <JGL/JGL.h>
#include <ReWindow/types/Key.h>
#include <JUI/Tween.hpp>
#include <JUI/JUI.hpp>
#include "JUI/DefaultStyle.hpp"
using namespace JGL;
namespace JUI
{
class ITween {};
template <typename T>
class Tween : public ITween
{
public:
std::function<T(void)> getter;
std::function<void(T)> setter;
float lifetime = 5;
float progress = 0;
T goal;
void Update(float dt)
{
}
};
/// Support Lerp/Tween of the following types:
/// int, float, UDim, UDim2, Color4, Vector2,
/// An enumeration for mouse buttons, used by JUI to decouple from external systems.
/// Some boilerplate is required in order to get input mechanisms up and running. See the demo files for reference.
enum class MouseButton {
Left = 1,
Middle = 2,
Right = 3
};
namespace JUI {
using namespace J3ML::Math;
using namespace J3ML::LinearAlgebra;
struct PaddedElementStyle {
};
struct EventArgs {
};
struct FocusedArgs {};
struct UnfocusedEventArgs {};
struct ChildAddedEventArgs {};
struct ChildRemovedEventArgs {};
/// TODO: Refactor the member functions providing the parent-child hierarchy into the Node mixin.
template <class TNode>
class Node {
public:
protected:
std::vector<TNode*> children;
TNode* parent;
};
/// Widget is the base class for all JUI elements and represents any object that can be in the tree hierarchy.
/// Some widgets are expressly used for layout, and therefore do not strictly speaking 'Appear' on screen.
/// Some widgets are expressly used for layout, and therefore do not strictly-speaking 'Appear' on screen.
/// Widgets exist in a tree hierarchy where each object has one parent, and an arbitrary number of children. No circular references are allowed.
class Widget
{
class Widget {
public:
/// The default constructor for Widget. Generally, JUI widgets will initialize member data,
/// and apply a default style in this constructor. Be aware of this, as this is not "idiomatic C++".
Widget();
/// Construct a new widget by specifying it's parent,
Widget(Widget* parent);
virtual ~Widget() {}
/// Construct a new widget by specifying its parent,
explicit Widget(Widget* parent);
virtual ~Widget() = default;
public:
#pragma region Events
/// An event that triggers when the widget is in-focus, generally when the mouse is hovering it.
/// (The actual trigger may differ for each widget)
Event<> Focused;
/// An event that triggers when the widget loses focus.
Event<> Unfocused;
/// This event triggers when a new widget is added to this widget, or any descendant of this widget.
Event<Widget *> DescendantAdded;
Event<Widget *> DescendantAdded; // TODO: Debug
/// This event triggers when a widget is removed from this widget's children, or any of it's childrens' children, and so forth.
Event<Widget *> DescendantRemoved;
Event<Widget *> DescendantRemoved; // TODO: Debug
/// This event triggers when the hierarchy this widget is contained within changes.
Event<Widget *, Widget*> AncestryChanged;
/// This event triggers when a widget is added to this widget's list of children.
@@ -87,26 +97,9 @@ namespace JUI
Event<Widget *> ChildRemoved;
/// This event triggers right before this widget gets deallocated.
Event<Widget *> Destroying;
#pragma endregion
public:
template <typename T>
void TweenTo(std::function<T()> getter, std::function<void(T)> setter, T goal) { }
/*template <>
void TweenTo(std::function<float()> getter, std::function<void(float)> setter, float goal) { }
template <>
void TweenTo(std::function<UDim()> getter, std::function<void(UDim)> setter, UDim goal) { }
template <>
void TweenTo(std::function<UDim2()> getter, std::function<void(UDim2)> setter, UDim2 goal) { }*/
void TweenFromTo(std::function<UDim2(void)> getter, std::function<void(UDim2)> setter, UDim2 start, UDim2 goal) {
}
#pragma region Hierarchy
/// Adds a given widget to this widget's list of children.
/// @return The widget in question.
Widget* Add(Widget* newChild);
@@ -124,6 +117,9 @@ namespace JUI
std::vector<Widget*> GetDescendants();
/// Returns a flat list of all nodes that are higher in the hierarchy list.
std::vector<Widget*> GetAncestors();
/// @returns the highest ancestor which is not the scene root.
Widget* GetHighestNonRootAncestor();
/// Returns the nodes directly descendant to this widget.
std::vector<Widget*> GetChildren();
@@ -133,10 +129,32 @@ namespace JUI
[[nodiscard]] virtual Vector2 GetAbsolutePosition() const;
[[nodiscard]] virtual float GetAbsoluteRotation() const;
[[nodiscard]] virtual Vector2 GetAbsoluteCentroid() const;
/// Returns the parent of this widget, or a nullptr if there is no parent.
/// @see GetFamilyTreeRoot().
Widget* GetParent() const;
/// Sets the parent object of this widget. Positioning and sizing of a widget is relative to it's parent.
void Parent(Widget*);
/// Returns true if this widget is a 'descendant' of the specified potential ancestor. Otherwise returns false.
bool IsDescendantOf(Widget *ancestor);
/// Returns true if this widget is a 'ancestor' of the specified potential descendant. Otherwise returns false.
bool IsAncestorOf(Widget *descendant);
/// Returns the first ancestor in this widgets hierarchy that does not have its own parent.
/// In a well-formed JUI menu, this **should** always be a Scene.
Widget* GetFamilyTreeRoot() const;
#pragma endregion
#pragma region Layout
/// Returns the menu-coordinates that are used to position this widget in relation to its parent.
/// @see class UDim2, Position(const UDim2&),
[[nodiscard]] UDim2 Position() const;
@@ -148,7 +166,7 @@ namespace JUI
/// @note The range of valid values is -MAX_INT to MAX_INT.
/// @note This does not manipulate the OpenGL Z buffer, rather, when rendering,
/// we sort the direct children of a widget widget and render.
int ZIndex() const;
[[nodiscard]] int ZIndex() const;
/// Sets this widgets z-index.
/// @see ZIndex().
@@ -167,18 +185,24 @@ namespace JUI
/// @see Size(), class UDim2.
void Size(const UDim2&);
/// Sets the parent object of this widget. Positioning and sizing of a widget is relative to it's parent.
void Parent(Widget*);
/// Creates and runs an animation on the Position of this widget.
Tween* TweenPosition(const UDim2& goal, TweenInfo params = {});
/// Returns true if this widget is a 'descendant' of the specified potential ancestor. Otherwise returns false.
bool IsDescendantOf(Widget *ancestor);
/// Returns true if this widget is a 'ancestor' of the specified potential descendant. Otherwise returns false.
bool IsAncestorOf(Widget *descendant);
/// Creates and runs an animation on the Size of this widget.
Tween* TweenSize(const UDim2& goal, TweenInfo params = {});
/// Determines the origin point of a Widget, relative to it's absolute size.
[[nodiscard]] Vector2 AnchorPoint() const;
///
void AnchorPoint(const Vector2 &point);
///
Tween* TweenAnchorPoint(const Vector2& goal, TweenInfo info = {});
#pragma endregion
#pragma region Padding
/// Returns the padding factor on the left of this widget.
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @see PaddingLeft(), class UDim.
@@ -212,6 +236,7 @@ namespace JUI
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @see PaddingLeft(), class UDim.
void PaddingBottom(const UDim &pad_bottom);
/// Sets the padding factor on the four respective sides of this widget.
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @param left
@@ -224,6 +249,17 @@ namespace JUI
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
void Padding(const UDim& padding);
Tween* TweenPaddingLeft(const UDim &goal, TweenInfo info = {});
Tween* TweenPaddingRight(const UDim &goal, TweenInfo info = {});
Tween* TweenPaddingTop(const UDim &goal, TweenInfo info = {});
Tween* TweenPaddingBottom(const UDim &goal, TweenInfo info = {});
Tween* TweenPadding(const UDim& goalLeft, const UDim& goalTop, const UDim& goalRight, const UDim& goalBottom, TweenInfo info = {});
Tween* TweenPadding(const UDim& goal, TweenInfo info = {});
#pragma endregion
#pragma region Margin
/// Returns the margin factor on the left of this widget.
[[nodiscard]] UDim MarginLeft() const;
/// Returns the margin factor on the top of this widget.
@@ -257,6 +293,17 @@ namespace JUI
/// @see Margin(const UDim&, const UDim&, const UDim&, const UDim&).
void Margin(const UDim& margin);
Tween* TweenMarginLeft(const UDim &goal, TweenInfo info = {});
Tween* TweenMarginTop(const UDim &goal, TweenInfo info = {});
Tween* TweenMarginBottom(const UDim &goal, TweenInfo info = {});
Tween* TweenMarginRight(const UDim &goal, TweenInfo info = {});
Tween* TweenMargin(const UDim& goalLeft, const UDim& goalTop, const UDim& goalRight, const UDim& goalBottom, TweenInfo info = {});
Tween* TweenMargin(const UDim& goal, TweenInfo info = {});
#pragma endregion
#pragma region Metadata
/// Returns this widgets mnemonic name.
/// Widgets can optionally be assigned a name that can be used to retrieve it from a widget tree node.
/// @see Name().
@@ -276,91 +323,128 @@ namespace JUI
/// @see IsVisible().
void Visible(bool enabled);
/// Returns the first ancestor in this widgets hierarchy that does not have its own parent.
/// In a well-formed JUI menu, this **should** always be a Scene.
Widget* GetFamilyTreeRoot() const;
/// Returns whether or not the mouse is inside this widget's approximate bounding-box.
/// Returns whether the mouse is inside this widget's approximate bounding-box.
bool IsMouseInside() const;
void AnchorPoint(const Vector2 &point);
/// Returns whether the mouse is inside the bounding box of one of this widget's children.
/// @param include_this Whether to return true if the mouse is inside this widget, if that condition is also met.
bool IsMouseInsideChildren(bool include_this = false) const;
/// Returns whether the mouse is inside the bounding box of one of this widget's descendants (children of children, recursive.)
/// @param include_this Whether to return true if the mouse is inside this widget, if that condition is also met.
bool IsMouseInsideDescendants(bool include_this = false) const;
int LayoutOrder() const { return layout_order; }
void LayoutOrder(int value) { layout_order = value;}
#pragma endregion
/// Returns the complete bounding box around this instance that will be rendered onto.
/// This differs from AbsolutePosition and AbsoluteSize in that they are computed for positioning elements relative to each other.
/// Only this method represents the full bounding box of the widget.
[[nodiscard]] virtual AABB2D GetActualRenderBounds() const;
AABB2D AbsoluteBounds() const;
void SetViewportSize(const Vector2& vps);
float ComputeElementPadding(float size, const UDim& padding);
Vector2 ComputeElementPadding(const Vector2& size, const UDim2& padding) const;
public:
// TODO: Consider calling J2D::Begin here.
virtual void PreDraw() {}
virtual void PostDraw() {}
// TODO: Consider calling J2D::End here.
virtual void InnerDraw() {}
/// Renders the widget to the current OpenGL Context using JGL.
/// The user should call this on their Scene instances only. JUI will handle the rest.
virtual void Draw();
/// Performs the update logic of the widget.
/// The user should call this on their Scene instances only. JUI will handle the rest.
virtual void Update(float delta);
/// Informs a widget that the mouse has been moved to a new location.
/// This is designed in such a way that the end-user can plug this into their existing code.
/// The user should call this on their Scene instances only. JUI will handle the rest.
/// See ReWindowIntegrationDemo for an example.
virtual void ObserveMouseMovement(const Vector2& latest_known_pos);
/// @return True if this widget, or one of its descendants should "consume" the input event,
/// meaning it no longer needs to be passed to the next widgets in the hierarchy.
/// @note It is acceptable for a widget to "observe" the input event, but not "consume" it.
virtual bool ObserveMouseMovement(const Vector2& latest_known_pos);
/// Informs a widget that a mouse button has pressed or released.
/// This is designed in such a way that the end-user can plug this into their existing code.
/// The user should call this on their Scene instances only. JUI will handle the rest.
/// See ReWindowIntegrationDemo for an example.
virtual void ObserveMouseInput(MouseButton btn, bool pressed);
/// @return True if this widget, or one of its descendants should "consume" the input event,
/// meaning it no longer needs to be passed to the next widgets in the hierarchy.
/// @note It is acceptable for a widget to "observe" the input event, but not "consume" it.
virtual bool ObserveMouseInput(MouseButton btn, bool pressed);
/// Informs a widget that the mouse wheel has been moved.
/// This is designed in such a way that the end-user can plug this into their existing code.
/// The user should call this on their Scene instances only. JUI will handle the rest.
/// See ReWindowIntegrationDemo for an example.
virtual bool ObserveMouseWheel(int mwheel);
/// Informs a widget that a key has been pressed or released.
/// This is designed in such a way that the end-user can plug this into their existing code.
/// The user should call this on their Scene instances only. JUI will handle the rest.
/// See ReWindowIntegrationDemo for an example.
virtual bool ObserveKeyInput(Key key, bool pressed);
//virtual void ObserveKeyInput()
protected:
void DrawChildWidgets();
void UpdateChildWidgets(float delta);
protected:
MouseButton mbtn;
MouseButton mbtn = MouseButton::Left;
bool mb_state = false;
bool prev_mb_state = false;
//int last_known_mouse_button;
//bool last_known_mouse_button_state;
Vector2 last_known_mouse_pos = {0,0};
UDim2 position = {0_px, 0_px};
UDim2 size = {50_px, 50_px};
Widget* parent = nullptr;
std::vector<Widget*> children;
std::vector<Widget*> children{};
std::vector<Tween*> tweens{};
float rotation = 0;
std::string name;
bool selected = false;
UDim pad_left = 0_px;
UDim pad_right = 0_px;
UDim pad_top = 0_px;
UDim pad_bottom = 0_px;
UDim margin_left = 0_px;
UDim margin_right = 0_px;
UDim margin_top = 0_px;
UDim margin_bottom = 0_px;
UDim pad_left = Style::BasePadding;
UDim pad_right = Style::BasePadding;
UDim pad_top = Style::BasePadding;
UDim pad_bottom = Style::BasePadding;
UDim margin_left = Style::BasePadding;
UDim margin_right = Style::BasePadding;
UDim margin_top = Style::BasePadding;
UDim margin_bottom = Style::BasePadding;
Vector2 anchor_point = {0.f, 0.f};
bool visible = true;
Widget* next = nullptr;
Widget* prev = nullptr;
int zindex = 0;
bool z_dirty = false;
int layout_order = 0;
Vector2 viewport_size{0,0};
bool is_mouse_inside = false;
/// Returns the amount of pixels this widget will be padded by from the top-left.
/// Generally, the widget will be shrunk and moved over by this amount, relative to the parent.
Vector2 GetAbsolutePaddingTopLeft() const;
/// Returns the amount of pixels this widget will be padded by from bottom-right.
/// Generally, the widget will be shrunk by twice this amount, relative to the parent.
Vector2 GetAbsolutePaddingBottomRight() const;
Vector2 GetAbsoluteMarginTopLeft();
Vector2 GetAbsoluteMarginBottomRight();
void UpdateTweens(float elapsed);
/// Calculate if mouse is inside this element at the start of it's update frame.
bool ComputeIsMouseInside() const;
};
}

View File

@@ -0,0 +1,72 @@
#pragma once
#include <Color4.hpp>
#include <Colors.hpp>
#include <JUI/UDim.hpp>
#include <JUI/UDim2.hpp>
namespace JUI {
namespace DefaultStyle {
const Color4 BackgroundColor = {128, 128, 128};
const Color4 BorderColor = {192, 192, 192};
const float BorderLineWidth = 1.f;
const UDim BasePadding = 0_px;
namespace Button {
const Color4 BaseBackgroundColor = Colors::White;
const Color4 BaseBorderColor = Colors::Black;
const Color4 HoverBackgroundColor = Colors::Blues::SkyBlue;
const Color4 HoverBorderColor = Colors::Blues::DarkSlateBlue;
const Color4 PressBackgroundColor = Colors::Blues::DarkSlateBlue;
const Color4 PressBorderColor = Colors::Blues::SkyBlue;
const Color4 DisableBackgroundColor = Colors::Gray;
const Color4 DisableBorderColor = Colors::Gray;
}
namespace Image
{
//const
}
namespace Text {
namespace H1 {
}
namespace H2 {
}
}
namespace InputForm {
const Color4 AutocompleteTextColor = Colors::Black;
}
namespace Collapsible {
const UDim HeaderHeight = 16_px;
}
namespace Checkbox {
const Color4 CheckmarkColor = Colors::Red;
}
namespace Window {
const UDim2 DefaultSize = {200_px, 200_px};
const Vector2 DefaultMinimumSize = {200, 200};
const int TitlebarHeight = 20;
const Color4 OutlineColor = {92, 92, 192};
const Color4 UnfocusedOutlineColor = Colors::Gray;
const Color4 ViewportBackgroundColor = Colors::DarkGray;
const int OutlineWidth = 2;
}
}
namespace ExperimentalStyle {
}
namespace Style = DefaultStyle;
}

View File

@@ -0,0 +1,5 @@
#pragma once
namespace JUI::Experimental {
}

View File

@@ -1,8 +1,41 @@
#pragma once
#include <jlog/Logger.hpp>
#include <Event.h>
namespace JUI
{
extern jlog::GenericLogger UILogs;
namespace JUI {
/// An enumeration for mouse buttons, used by JUI to decouple from external systems.
/// Some boilerplate is required in order to get input mechanisms up and running. See the demo files for reference.
enum class MouseButton {
Left = 1,
Middle = 2,
Right = 3
};
/// Designates an element as laid out horizontally or vertically.
enum class Orientation { Horizontal, Vertical };
/// Determines how a line element is decorated.
enum class LineFillMode { Solid, Dotted, Dashed };
/// Logger class for JUI. @see project jlog
class JUILogger : public jlog::GenericLogger {
public:
Event<std::string, Color4> OnLog;
JUILogger(const std::string& context,
std::ofstream& file,
Color4 contextColor = Colors::White,
Color4 timestampColor = Colors::Purples::Fuchsia,
Color4 locationColor = Colors::Pinks::Pink,
Color4 pointerColor = Colors::Pinks::LightPink,
Color4 messageColor = Colors::Greens::LightGreen);
void Log(const std::string& message, const std::source_location &location = std::source_location::current(), const jlog::Timestamp &ts = jlog::Timestamp()) override;
};
extern JUILogger UILogs;
}

View File

@@ -14,12 +14,28 @@
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <JUI/Base/Widget.hpp>
namespace JUI
{
#include "Hoverable.hpp"
namespace JUI {
struct ClickEventArgs {
public:
protected:
private:
};
struct ReleaseEventArgs {
public:
protected:
private:
};
/// A mixin helper class that provides behavior and events for clickable widgets.
class Clickable
{
class Clickable {
public:
Event<std::optional<ClickEventArgs>> Clicked;
Event<ReleaseEventArgs> Released;
Event<Vector2, MouseButton> OnClickEvent;
Event<Vector2, MouseButton, bool> OnReleaseEvent;
public:
@@ -35,4 +51,4 @@ namespace JUI
bool clicked = false;
bool click_debounce = false;
};
}
}

View File

@@ -0,0 +1,8 @@
//
// Created by josh on 4/11/25.
//
#ifndef DRAGANDDROPRECEIVER_HPP
#define DRAGANDDROPRECEIVER_HPP
#endif //DRAGANDDROPRECEIVER_HPP

View File

@@ -0,0 +1,8 @@
//
// Created by josh on 4/11/25.
//
#ifndef DRAGANDDROPSOURCE_HPP
#define DRAGANDDROPSOURCE_HPP
#endif //DRAGANDDROPSOURCE_HPP

View File

@@ -0,0 +1,62 @@
/// Josh's User Interface Library
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Resizable.hpp
/// @desc Resizable Mixin Helper class
/// @edit 2024-10-11
#pragma once
#include <Event.h>
#include <J3ML/LinearAlgebra/Vector2.hpp>
namespace JUI
{
/// A mixin helper class that enables a widget to be resized by the mouse.
class Focusable {
public:
Event<> OnGrabFocus;
Event<> OnDropFocus;
[[nodiscard]] bool Focused() const;
[[nodiscard]] bool IsFocused() const { return focused;}
[[nodiscard]] bool HasFocus() const { return focused;}
virtual void GrabFocus() { SetFocused(true);}
virtual void DropFocus(const Vector2& point) {
if (do_focus_cooldown_hack && focus_hack_cooldown > time_focused)
return;
SetFocused(false);
}
virtual void SetFocused(bool value) {
focused = value;
if (do_focus_cooldown_hack && value) time_focused = 0;
}
virtual void Update(float delta) {
if (do_focus_cooldown_hack)
time_focused += delta;
}
void DoFocusHackCooldown(bool value) { do_focus_cooldown_hack = value; }
bool DoFocusHackCooldown() const { return do_focus_cooldown_hack; }
float FocusHackCooldown() const { return focus_hack_cooldown; }
void FocusHackCooldown(float value) {
focus_hack_cooldown = value;
}
protected:
bool focused = false;
float time_focused = 0;
bool do_focus_cooldown_hack = false;
float focus_hack_cooldown = 0.1f;
};
}

View File

@@ -18,24 +18,44 @@
namespace JUI
{
/// Interface for anything that can report mouse presence.
class IMouseAware {
public:
virtual ~IMouseAware() = default;
virtual bool IsMouseAware() const = 0;
};
/// A mixin helper class that provides behavior for hoverable objects.
/// A hoverable object pays attention to when the mouse enters and leaves it's bounds.
class Hoverable {
public:
Event<Vector2> OnHoverEvent;
Event<Vector2> OnExitEvent;
Event<Vector2> OnHoverChildrenEvent;
Event<Vector2> OnExitChildrenEvent;
Event<Vector2> OnHoverDescendantsEvent;
Event<Vector2> OnExitDescendantsEvent;
public:
bool IsHovered() const { return hovered; };
public:
virtual void OnHover(const Vector2& MousePos);
virtual void OnExit(const Vector2& MousePos);
virtual void OnHoverChildren(const Vector2& mouse);
virtual void OnHoverDescendants(const Vector2& mouse);
virtual void OnExitChildren(const Vector2& mouse);
virtual void OnExitDescendants(const Vector2& mouse);
void Update(const Vector2& m_pos, float delta);
virtual void SetTooltip(const std::string& content, float delay) {}
void Update(bool mouse_inside, float delta);
/// @note This member function **must** be implemented for classes that implement Hoverable.
//[[nodiscard]] virtual bool IsMouseInside() const = 0;
//virtual void SetTooltip(const std::string& content, float delay) {}
protected:
Tooltip* tooltip = nullptr;
float tooltip_threshold = 0.f;
float tooltip_limit = 0.125f;
//Tooltip* tooltip = nullptr;
//float tooltip_threshold = 0.f;
//float tooltip_limit = 0.125f;
bool hovered = false;
bool hover_debounce = false;
};

View File

@@ -0,0 +1,5 @@
#pragma once
namespace JUI {
}

View File

@@ -0,0 +1,57 @@
/// Josh's User Interface Library
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
/// Developed and Maintained by Josh O'Leary @ Redacted Software
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Pinnable.hpp
/// @desc A mixin class that provides a widget with the ability to be "pinned": locked open and fixed in place.
/// @edit 2025-04-30
#pragma once
#include <Event.h>
#include <JUI/UDim2.hpp>
namespace JUI {
/// The pinnable mixin class. Designates a widget as "pinned" or not, which, depending on the derived classes' semantics, may lock it open or in it's current place.
class Pinnable {
public:
Pinnable() = default;
virtual ~Pinnable() = default;
Event<> OnPin;
Event<> OnUnpin;
/// Sets the object as "Pinned". Depending on the widget, position and visibility status will be locked.
/// @see Unpin(), SetPinned().
virtual void Pin();
/// Sets the object as "Unpinned".
/// @see Pin(), SetPinned().
virtual void Unpin();
/// Sets the objects pinned status manually.
void SetPinned(bool pinned);
/// Called when the pinned status changes, and triggers callback event signals.
virtual void OnPinStatusChanged(bool new_status);
bool Pinned() const;
protected:
// TODO: Implement position pinning, both relative to parent and global.
bool pin_point_is_relative = true;
UDim2 pinned_point_relative{};
Vector2 pinned_point_global{};
bool pinned_visibility = false;
private:
bool pinned = false;
};
}

View File

@@ -0,0 +1,14 @@
#pragma once
namespace JUI {
struct ButtonStyle {
RectStyle base_style;
RectStyle hovered_style;
RectStyle pressed_style;
RectStyle disabled_style;
};
class ButtonStyler {
};
}

View File

@@ -0,0 +1,15 @@
#pragma once
namespace JUI {
struct CheckboxStyle {
RectStyle unchecked_style;
RectStyle checked_color;
RectStyle disabled_style;
};
class CheckboxStyler {
public:
protected:
private:
};
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include <Color4.hpp>
#include <Colors.hpp>
namespace JUI {
struct RectStyle {
float border_width = 1.f;
Color4 border_color {192, 192, 192};
Color4 bg_color {64, 64, 64};
float corner_rounding = 0.f;
enum BorderMode border_mode;
enum CornerRoundingMode corner_mode;
};
class RectStyler {
public:
#pragma region Getters
virtual Color4 BGColor() const = 0;
virtual Color4 BorderColor() const = 0;
virtual enum BorderMode BorderMode() const = 0;
virtual float BorderWidth() const = 0;
virtual float CornerRounding() const = 0;
#pragma endregion
#pragma region Setters
virtual void BGColor(const Color4& color) = 0;
virtual void BorderColor(const Color4& color) = 0;
virtual void BorderMode(const enum BorderMode& borderMode) = 0;
virtual void BorderWidth(float width) = 0;
#pragma endregion
void Style(const RectStyle& value) {
style = value;
}
protected:
float border_width = 1.f;
Color4 border_color {192, 192, 192};
enum BorderMode border_mode;
float corner_rounding = 0.f;
RectStyle style;
private:
};
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include <Color4.hpp>
#include <Colors.hpp>
#include "JGL/JGL.h"
#include "JGL/types/Font.h"
namespace JUI {
struct TextStyle {
Color4 color;
JGL::Font font;
int size;
};
/// Defines interface for widgets with textual elements.
/// Any object that renders text will use these methods to apply style.
class TextStyler {
public:
virtual ~TextStyler() = default;
public:
#pragma region Getters
/// @returns the font of this widget.
virtual JGL::Font Font() const;
/// @return The text-point-size of this widget.
virtual int TextSize() const;
/// @return The text-color of this widget. @see class Color4.
virtual Color4 TextColor() const;
#pragma endregion
public:
#pragma region Setters
virtual void Font(const JGL::Font& value);
virtual void TextSize(int value);
virtual void TextColor(const Color4& color);
virtual void Style(const JGL::Font& font, int size, const Color4& color);
#pragma endregion
protected:
JGL::Font font = Fonts::Jupiteroid;
int text_size = 12;
Color4 text_color = Colors::White;
};
}

137
include/JUI/Tween.hpp Normal file
View File

@@ -0,0 +1,137 @@
#pragma once
#include <functional>
#include "Event.h"
namespace JUI
{
using EasingFunc = std::function<float(float)>;
using TweenTickFunc = std::function<void(float, float)>;
namespace EasingFunctions
{
float EaseInOutLinear(float t);
/// Speed is determined by a sine wave for gentle easing motion.
float EaseInSine(float t);
/// Speed is determined by a sine wave for gentle easing motion.
float EaseOutSine(float t);
/// Speed is determined by a sine wave for gentle easing motion.
float EaseInOutSine(float t);
/// Similar to Sine but with a slightly sharper curve based on quadratic interpolation.
float EaseInQuad(float t);
/// Similar to Sine but with a slightly sharper curve based on quadratic interpolation.
float EaseOutQuad(float t);
/// Similar to Sine but with a slightly sharper curve based on quadratic interpolation.
float EaseInOutQuad(float t);
/// Similar to Quad but with a slightly sharper curve based on cubic interpolation.
float EaseInCubic(float t);
/// Similar to Quad but with a slightly sharper curve based on cubic interpolation.
float EaseOutCubic(float t);
/// Similar to Quad but with a slightly sharper curve based on cubic interpolation.
float EaseInOutCubic(float t);
/// Similar to Cubic but with an even sharper curve based on quartic interpolation.
float EaseInQuart(float t);
/// Similar to Cubic but with an even sharper curve based on quartic interpolation.
float EaseOutQuart(float t);
/// Similar to Cubic but with an even sharper curve based on quartic interpolation.
float EaseInOutQuart(float t);
/// Similar to Quart but with an even sharper curve based on quintic interpolation.
float EaseInQuint(float t);
/// Similar to Quart but with an even sharper curve based on quintic interpolation.
float EaseOutQuint(float t);
/// Similar to Quart but with an even sharper curve based on quintic interpolation.
float EaseInOutQuint(float t);
/// The sharpest curve based on exponential interpolation.
float EaseInExpo(float t);
/// The sharpest curve based on exponential interpolation.
float EaseOutExpo(float t);
/// The sharpest curve based on exponential interpolation.
float EaseInOutExpo(float t);
/// Follows a circular arc, such that acceleration is more sudden and deceleration more gradual versus Quint or Exponential.
float EaseInCirc(float t);
/// Follows a circular arc, such that acceleration is more sudden and deceleration more gradual versus Quint or Exponential.
float EaseOutCirc(float t);
/// Follows a circular arc, such that acceleration is more sudden and deceleration more gradual versus Quint or Exponential.
float EaseInOutCirc(float t);
/// Slightly overshoots the target, then backs into place.
float EaseInBack(float t);
/// Slightly overshoots the target, then backs into place.
float EaseOutBack(float t);
/// Slightly overshoots the target, then backs into place.
float EaseInOutBack(float t);
float EaseInElastic(float t);
float EaseOutElastic(float t);
float EaseInOutElastic(float t);
float EaseInBounce(float t);
float EaseOutBounce(float t);
float EaseInOutBounce(float t);
}
struct TweenInfo {
float time = 1.f;
float delay = 0.f;
int repeats = 0;
bool reverse = false;
EasingFunc easing = &EasingFunctions::EaseInOutLinear;
bool overwritable = true;
};
/// A class that represents an animation-in-action.
class Tween {
public:
Tween(TweenTickFunc tick_func);
Tween(TweenTickFunc tick_func, const TweenInfo& info) {
this->tick_func = tick_func;
this->time = info.time;
this->delay_time = info.delay;
this->repeat_count = info.repeats;
this->reverses = info.reverse;
this->easing_func = info.easing;
this->overwritable = info.overwritable;
}
Event<> Completed;
void Update(float elapsed);
std::function<float(float)> easing_func = &EasingFunctions::EaseInOutLinear;
std::function<void(float, float)> tick_func;
/// Duration of the tween, in seconds.
float time = 5;
/// Time of delay until the tween begins, in seconds.
float delay_time = 0;
/// Number of times the tween repeats. -1 indicates indefinite repetition.
int repeat_count = 0; // TODO: Implement
/// Whether or not the tween interpolates in reverse once the initial tween completes.
bool reverses = false; // TODO: Implement
float progress = 0;
bool alive = true;
bool paused = false;
bool completed = false;
bool overwritable = true;
void Cancel();
void ForceFinish();
void Pause() { paused = true; }
void Resume() { paused = false; }
void Start();
bool Paused() const;
bool HasCompleted() const;
};
}

View File

@@ -11,6 +11,8 @@
#pragma once
#include "J3ML/J3ML.hpp"
namespace JUI
{
/// A coordinate system data type for user interfaces specifically.
@@ -24,6 +26,9 @@ namespace JUI
UDim() : Pixels(0), Scale(0.f) {}
UDim(int pixels, float scale) : Pixels(pixels), Scale(scale) {}
UDim(const UDim& u) = default;
static UDim FromPixels(int pixels);
static UDim FromScale(float scale);
public:
UDim operator + (const UDim& rhs) const;
@@ -32,6 +37,10 @@ namespace JUI
UDim operator * (float rhs) const;
UDim operator / (float rhs) const;
bool Equals(const UDim& rhs, float epsilon = 1e-3f);
UDim Lerp(const UDim& goal, float t);
};
namespace UDimLiterals {

View File

@@ -17,7 +17,6 @@
namespace JUI
{
using namespace J3ML::LinearAlgebra;
/// Funky Coordinate system purpose-built for screen positioning
@@ -26,6 +25,17 @@ namespace JUI
/// @see UDim.hpp
class UDim2
{
public: // Constants
static const UDim2 Center;
static const UDim2 TopLeft;
static const UDim2 BottomRight;
static const UDim2 BottomLeft;
static const UDim2 TopRight;
static const UDim2 TopCenter;
static const UDim2 LeftCenter;
static const UDim2 BottomCenter;
static const UDim2 RightCenter;
public: // Properties
UDim X;
UDim Y;
@@ -42,6 +52,10 @@ namespace JUI
static UDim2 FromScale(float x, float y);
UDim2 Lerp(const UDim2& goal, float t);
bool Equals(const UDim2& rhs, float epsilon = 1e-3f);
public: // Operators
UDim2 operator +(const UDim2& rhs) const;

View File

@@ -0,0 +1,94 @@
#pragma once
#include <JUI/Base/Widget.hpp>
namespace JUI {
/// An invisible, point-like widget which is used to attach decorator-type widgets at a given point.
/// For example: The CurveSegment Widget uses two Anchors for it's endpoints.
class Anchor : public Widget {
public:
Anchor() : Widget() {
}
explicit Anchor(Widget* parent) : Anchor() {
Parent(parent);
};
explicit Anchor(Widget* parent, const UDim2& position) : Anchor(parent) {
Position(position);
Size({0_px, 0_px});
}
void Update(float elapsed) override {
}
void Draw() override {
Color4 debug_color = Colors::Red;
float debug_size = 1;
J2D::FillCircle(debug_color, GetAbsolutePosition(), debug_size, 5);
}
Vector2 Point() { return GetAbsolutePosition(); }
protected:
private:
};
class Decorator : public Widget {
public:
Decorator() : Widget(){
}
explicit Decorator(Widget* parent) : Decorator() {
Parent(parent);
}
};
class ArrowDecorator : public Decorator {
public:
explicit ArrowDecorator(Widget* parent, Anchor* a, Anchor* b) : Decorator() {
Parent(parent);
origin = a;
goal = b;
}
void Draw() override {
auto goal_point = goal->Point();
auto origin_point = origin->Point();
using namespace J3ML;
J2D::DrawLine(color, origin->Point(), goal->Point(), line_width);
Vector2 direction = ( origin->Point() - goal->Point()).Normalized();
Vector2 triangle_base_pt = goal->Point() + (direction*arrowhead_size);
//J2D::DrawPoint(Colors::Yellow, triangle_base_pt, 4);
Vector2 tri_left = triangle_base_pt + (direction.Rotated90CW() *arrowhead_size);
Vector2 tri_right = triangle_base_pt + (direction.Rotated90CCW()*arrowhead_size);
Vector2 tri_top = goal->Point();
J2D::DrawLine(color, tri_top, tri_left, arrowhead_size);
J2D::DrawLine(color, tri_top, tri_right, arrowhead_size);
}
protected:
float arrowhead_size;
float line_width;
Color4 color;
Anchor* origin;
Anchor* goal;
private:
};
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <JUI/Widgets/Window.hpp>
namespace JUI
{
struct BindAction
{
std::string id;
std::string display_name;
std::vector<std::string> key_ids;
};
class BindMenu : public Window {
public:
explicit BindMenu();
void AddAction(std::string id, std::string disp_name);
};
}

View File

@@ -105,17 +105,18 @@ namespace JUI
void OnRelease(const Vector2& mouse_pos, const MouseButton& bnt, bool still_hovering) override;
void OnHover(const J3ML::LinearAlgebra::Vector2 &MousePos) override;
void OnExit(const J3ML::LinearAlgebra::Vector2 &MousePos) override;
void SetTooltip(const std::string &content, float delay = 0.2f) override;
//void SetTooltip(const std::string &content, float delay = 0.2f) override;
protected:
bool disabled = false;
Color4 hover_bg = Colors::Blues::SkyBlue;
Color4 hover_border = Colors::Blues::DarkSlateBlue;
Color4 pressed_bg = Colors::Blues::DarkSlateBlue;
Color4 pressed_border = Colors::Blues::SkyBlue;;
Color4 disabled_bg = Colors::Gray;
Color4 disabled_border = Colors::Gray;
Color4 base_bg = Colors::White;
Color4 base_border = Colors::Black;
Color4 hover_bg = Style::Button::HoverBackgroundColor;
Color4 hover_border = Style::Button::HoverBorderColor;
Color4 pressed_bg = Style::Button::PressBackgroundColor;
Color4 pressed_border = Style::Button::PressBorderColor;
Color4 disabled_bg = Style::Button::DisableBackgroundColor;
Color4 disabled_border = Style::Button::DisableBorderColor;
Color4 base_bg = Style::Button::BaseBackgroundColor;
Color4 base_border = Style::Button::BaseBorderColor;
void UpdateVisualState();
};

View File

@@ -1,24 +0,0 @@
/// Josh's User Interface Library
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Canvas.hpp
/// @desc A 2D drawable canvas widget.
/// @edit 2024-11-27
#pragma once
#include <JUI/Widgets/Rect.hpp>
namespace JUI
{
class Canvas : public Rect
{
public:
protected:
private:
};
}

View File

@@ -13,94 +13,44 @@
#include <JUI/Base/Widget.hpp>
#include <JUI/Widgets/Rect.hpp>
#include "JUI/Mixins/Clickable.hpp"
#include "JUI/Mixins/Hoverable.hpp"
#include "Button.hpp"
#include "JUI/Base/ImageBase.hpp"
#include <JUI/Mixins/Clickable.hpp>
#include <JUI/Mixins/Hoverable.hpp>
#include <JUI/Widgets/Button.hpp>
#include <JUI/Base/ImageBase.hpp>
#include <JUI/Widgets/ImageButton.hpp>
namespace JUI
{
class Checkbox : public Button {
namespace JUI {
// TODO: Find a nice way to implement a checkmark.
class CheckboxBase {
public:
Checkbox() : Button() {
}
explicit Checkbox(Widget *parent) : Checkbox()
{
this->Parent(parent);
}
void Update(float delta) override
{
Button::Update(delta);
}
void OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &bnt, bool still_hovering) override
{
Button::OnRelease(mouse_pos, bnt, still_hovering);
checked = !checked;
}
void InnerDraw() override
{
Rect::InnerDraw();
if (checked)
{
J2D::Begin();
Vector2 check_padding = {2, 2};
RectBase::Draw(check_color, check_color, GetAbsolutePosition()+check_padding, GetAbsoluteSize()-(check_padding*2));
J2D::End();
}
}
void Draw() override
{
if (!visible)
return;
J2D::Begin();
Vector2 abs_pos = GetAbsolutePosition();
Vector2 abs_size = GetAbsoluteSize();
auto root_size = GetFamilyTreeRoot()->GetAbsoluteSize();
GLint *old_scissor_bounds;
bool clip_was_enabled;
if (clips_descendants) {
clip_was_enabled = glIsEnabled(GL_SCISSOR_TEST);
if (clip_was_enabled)
glGetIntegerv(GL_SCISSOR_BOX, old_scissor_bounds);
float presumed_screen_height = 600;
glScissor(abs_pos.x, presumed_screen_height-abs_size.y-abs_pos.y, abs_size.x, abs_size.y);
glEnable(GL_SCISSOR_TEST);
}
RectBase::Draw(abs_pos, abs_size);
// Draw Child Elements with scissor clipping still active
Widget::Draw();
// Returns clip to previous state
if (clips_descendants)
{
//glScissor(old_scissor_bounds[0], old_scissor_bounds[1], old_scissor_bounds[2], old_scissor_bounds[3]);
if (!clip_was_enabled) {}
glDisable(GL_SCISSOR_TEST);
}
J2D::End();
}
[[nodiscard]] Color4 CheckedColor() const { return check_color; }
void CheckedColor(const Color4& color) { check_color = color; }
bool IsChecked() const { return checked;}
void SetChecked(bool value) { checked = value; }
Tween* TweenCheckedColor(const Color4& goal, TweenInfo info = {});
protected:
bool checked = false;
Color4 check_color = Style::Checkbox::CheckmarkColor;
Color4 uncheck_color = Style::BackgroundColor;
};
class Checkbox : public Button, public CheckboxBase {
public:
Checkbox();
explicit Checkbox(Widget *parent);
void Update(float delta) override;
void OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &bnt, bool still_hovering) override;
void InnerDraw() override;
protected:
bool checked;
Color4 check_color = Colors::Red;
private:
};
class ImageCheckbox : public ImageButton, public CheckboxBase {
public:
protected:
private:
};
}

View File

@@ -1,5 +1,45 @@
/// Josh's User Interface Library
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2025 Redacted Software
/// This work is dedicated to the public domain.
/// @file Collapsible.cpp
/// @desc A rectangular widget with a clickable header that enables hiding and showing the contents.
/// @edit 2025-02-14
#pragma once
namespace JUI
{
#include <JUI/Widgets/Rect.hpp>
#include <JUI/Widgets/TextButton.hpp>
namespace JUI {
class Collapsible : public Rect {
public:
Collapsible();
explicit Collapsible(Widget* parent);
TextButton* Header();
Rect* ContentBox();
void Collapse();
void Expand();
[[nodiscard]] bool Collapsed() const;
UDim HeaderHeight() const;
void HeaderHeight(const UDim& value);
void Title(const std::string& value);
std::string Title() const;
protected:
TextButton* header = nullptr;
Rect* content_box = nullptr;
TextRect* lil_arrow = nullptr;
bool collapsed = false;
UDim2 saved_size;
UDim header_height = Style::Collapsible::HeaderHeight;
Tween* resize_anim = nullptr;
};
}

View File

@@ -1,8 +1,79 @@
//
// Created by dawsh on 8/1/24.
//
#pragma once
#ifndef JUI_COLORPICKERTOOL_HPP
#define JUI_COLORPICKERTOOL_HPP
#include <JUI/Widgets/Rect.hpp>
#include <JUI/Widgets/TextRect.hpp>
#include <JUI/Widgets/ListLayout.hpp>
#include <JUI/Widgets/Text.hpp>
#endif //JUI_COLORPICKERTOOL_HPP
#include "../Style/TextStyler.hpp"
#include <JUI/Widgets/Slider.hpp>
#include "Window.hpp"
/// TODO: Move to a utility library or something...
/// Converts a set of R,G,B values to it's hexadecimal string representation.
std::string rgb2hex(int r, int g, int b, bool with_head);
namespace JUI {
/// A JUI Widget that displays a HSV color input dialog.
class ColorPicker : public Rect, public TextStyler {
public:
ColorPicker();
explicit ColorPicker(Widget* parent);
/// Invoked when the color value is changed by the user.
/// @param Color4 The new color value.
Event<Color4> OnColorValueChanged;
/// Sets the font of the text elements contained in this widget.
void Font(const JGL::Font &value) override;
/// Sets the point size of the text elements contained in this widget.
void TextSize(int size) override;
/// Sets the point size of the text elements contained in this widget.
void TextColor(const Color4 &color) override;
void SetColorValue(const Color4& color);
Color4 GetColorValue() const;
float Hue() const { return hue;}
float Sat() const { return sat;}
float Val() const { return bri;}
protected:
/// Calculates the colors of the label, and the inverse color for the text.
void RecomputeVisuals();
/// Applies this widget's specific styling to it's slider sub-components.
void StyleSlider(Slider* slider);
JUI::Slider* hue_slider = nullptr;
JUI::Slider* sat_slider = nullptr;
JUI::Slider* bri_slider = nullptr;
JUI::Text* hue_label = nullptr;
JUI::Text* sat_label = nullptr;
JUI::Text* bri_label = nullptr;
JUI::TextRect* hex_label = nullptr;
float hue = 0;
float sat = 1.f;
float bri = 1.f; // AKA val
private:
};
class ColorPickerDialog : public Window {
public:
protected:
private:
};
}

View File

@@ -0,0 +1,63 @@
#pragma once
#include <JUI/Widgets/Window.hpp>
#include <JUI/Widgets/TextInputForm.hpp>
#include <JUI/Widgets/ScrollingRect.hpp>
#include <JUI/Widgets/ListLayout.hpp>
namespace JUI {
// TODO: Implement "Rich Text" message support.
// TODO: Implement command autocomplete helper API.
// TODO: Implement API for user-projects to bind commands.
// TODO: Implement API for user-projects to bind loggers.
// TODO: Implement message history.
// TODO: As of now, the API user is in charge of parsing and executing commands.
// TODO: A fairly-generic CommandInterpreter which suits our purposes.
// TODO: <command> <arg1> <arg2> <arg3>
// TODO: Tight JSON integration for specifying complex metadata in commands.
/// A generic "Game Console", which provides a log history, and input box for user-defined commands.
class CommandLine : public Window {
public:
/// This event is invoked when the user has sent a message to the command line.
Event<std::string> OnInput;
/// The default constructor initializes the layout and events of child elements.
/// This constructor is a delegate called by the explicit constructor with parent parameter.
CommandLine();
/// Constructs a command line by specifying the widget it should be parented to.
/// @param parent
explicit CommandLine(Widget* parent);
/// Adds a message to the CommandLine message history.
/// @param message
/// @param color
void Log(const std::string& message, const Color4& color = Colors::White);
/// Sets the "open"-ness of the window widget. The widget will be invisible and ignore input if it is not open.
void SetOpen(bool openVal) override;
/// Passes input events down the widget hierarchy.
/// @return True if this widget will "consume" the input.
bool ObserveKeyInput(Key key, bool pressed) override;
/// Invoked when a message is sent to the command line.
virtual void OnInputBoxSend(const std::string& message);
protected:
TextInputForm* input_box;
ScrollingRect* message_log_box;
VerticalListLayout* message_log_list;
protected:
int index = 0;
int input_form_height = 20;
int message_height = 16;
std::vector<std::string> msg_history;
private:
};
}

View File

@@ -0,0 +1,96 @@
/// Josh's User Interface Library
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Widget.hpp
/// @desc Base Widget Class - All JUI widgets extend their behavior from this class.
/// @edit 2024-10-11
#pragma once
#include <JUI/Widgets/TextButton.hpp>
#include "ListLayout.hpp"
#include "Separator.hpp"
#include "JUI/Mixins/Pinnable.hpp"
#include "../Style/TextStyler.hpp"
namespace JUI {
class ContextMenu;
// TODO: Move to NestableMenu.hpp after resolving circular dependency.
/// An abstract class that defines the interface for nestable menus - menu objects that contain similar sub-menus.
class NestableMenu {
virtual ContextMenu* AddSubmenu(const std::string& text) = 0;
virtual TextButton* AddButton(const std::string& text) = 0;
virtual TextButton* AddButton(const std::string& text, const std::function<void()>& callback) = 0;
virtual Separator* AddSeparator(const UDim& size = 5_px) = 0;
};
/// A vertically-descending list menu of button objects, which supports sub-menus much like class UtilityBar.
/// @see UtilityBar
class ContextMenu : public Rect, public Pinnable, public NestableMenu, public TextStyler {
public:
/// The default constructor initializes base state of the widget.
ContextMenu();
/// Constructs this object by explicitly specifying the parent element.
explicit ContextMenu(Widget* parent);
public:
Event<> PresumeMouseFocusLost;
public:
/// Adds a new button to this ContextMenu.
/// @param name The label to apply to the new button.
TextButton* AddButton(const std::string &name) override;
/// Adds a new button to this ContextMenu.
/// @param name The label to apply to the new button.
/// @param callback The function to run when the button is clicked.
TextButton* AddButton(const std::string& name, const std::function<void()> &callback) override;
/// Adds a separator to this ContextMenu.
/// @param size The height of this separator.
Separator* AddSeparator(const UDim& size = 5_px) override;
/// Adds a button, which opens a submenu, to this ContextMenu. Only the submenu is returned.
ContextMenu* AddSubmenu(const std::string& name) override;
/// Sets the text size of the buttons and sub-menus managed by this object.
void TextSize(int value) override;
/// Sets the text color of the buttons and sub-menus managed by this object.
void TextColor(const Color4& color) override;
/// Sets the font of the buttons and sub-menus managed by this object.
void Font(const JGL::Font& value) override;
/// Sets whether the widget will automatically hide itself when the mouse leaves its bounding box.
void CloseOnHoverLoss(bool value);
/// @return Whether the widget will automatically hide itself when the mouse leaves its bounding box.
[[nodiscard]] bool CloseOnHoverLoss() const;
void Update(float delta) override;
protected:
VerticalListLayout* layout;
bool close_on_hover_loss = true;
float aggregate_height = 0;
float estimated_width = 0;
float mouse_outside_tests = 0;
// TODO: This is handled the same way as in UtilityBar, proposed to abstract it away into NestableMenu.
std::vector<TextButton*> buttons;
std::vector<ContextMenu*> submenus;
private:
};
}

View File

@@ -0,0 +1,150 @@
/// Josh's User Interface Library
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file FileDialog.hpp
/// @desc A dialog window that shows a selection of files on disk, and a form for naming, used for file loading and saving.
/// @edit 2024-5-29
#pragma once
#include <JUI/Widgets/Window.hpp>
#include <JUI/Widgets/UtilityBar.hpp>
#include <JUI/Widgets/ScrollingRect.hpp>
namespace JUI
{
class FileListing : public JUI::Widget { };
// TODO: Implement SortBy enumeration - Date, Size, Name, Type
class FileDialogWindow : public JUI::Window
{
public:
Event<> UserCompleted;
Event<> UserConfirmed;
Event<> UserCancelled;
Event<std::string> FileSelected;
FileDialogWindow() : Window() {
Title("File Dialog");
//auto* toolbar = new JUI::UtilityBar(this->Content());
auto* file_listing = new JUI::ScrollingRect(this->Content());
file_listing->Size({100_percent, 100_percent-20_px});
file_layout = new JUI::VerticalListLayout(file_listing);
}
/// Tree system
std::string format_perms(const std::filesystem::perms& p) const {
using std::filesystem::perms;
auto parse = [=](char op, perms perm) {
return (perms::none == (perm & p) ? '-' : op);
};
std::string perm_string = "";
perm_string += parse('r', perms::owner_read);
perm_string += parse('w', perms::owner_write);
perm_string += parse('x', perms::owner_exec);
perm_string += parse('r', perms::group_read);
perm_string += parse('w', perms::group_write);
perm_string += parse('x', perms::group_exec);
perm_string += parse('r', perms::others_read);
perm_string += parse('w', perms::others_write);
perm_string += parse('x', perms::others_exec);
return perm_string;
}
std::string format_size(uintmax_t size) const {
bool addSpace = false;
int precision = 2;
std::vector<std::string> units = {"B", "KB", "GB", "TB", "PB", "EB", "ZB", "YB"};
if (Math::Abs(size) < 1) return std::to_string(size) + (addSpace ? " " : "") + units[0];
auto exponent = Math::Min<uintmax_t>(Math::Floor(Math::Log10(size < 0 ? -size : size) / 3), units.size()-1);
auto n = (size < 0 ? -size : size) / std::pow(1000, exponent);
return (size < 0 ? "-" : "") + std::format("{}", n) + (addSpace ? " ": "") + units[exponent];
}
JUI::Rect* CreateFileEntry(const std::filesystem::directory_entry& entry) {
auto* rect = new JUI::TextButton(file_layout);
rect->LayoutOrder(index);
rect->Size({100_percent, 20_px});
std::string entry_type = entry.is_directory() ? "directory" : "file";
std::string perms = format_perms(entry.status().permissions());
auto file_size = (entry.is_directory() ? 0 : entry.file_size());
auto file_size_str = format_size(file_size);
std::filesystem::file_time_type timestamp = (entry.is_directory() ? std::filesystem::file_time_type{} : entry.last_write_time());
std::string label = std::format("{} {} {} {} {}", entry.path().filename().string(), file_size_str, entry_type, perms, timestamp);
rect->Content(label);
rect->BaseBGColor(Colors::Gray);
index++;
rect->Clicked += [this, entry] (auto ev) mutable {
FileSelected(entry.path().string());
};
return rect;
}
FileDialogWindow(const std::filesystem::path& root) : FileDialogWindow() {
std::cout << root.relative_path() << std::endl;
std::cout << root.root_directory() << std::endl;
std::cout << root.filename() << std::endl;
std::cout << root.parent_path() << std::endl;
std::cout << root.string() << std::endl;
std::cout << std::filesystem::current_path() << std::endl;
Title(std::format("'{}' in '{}' - File Dialog", root.relative_path().string(), std::filesystem::current_path().string()));
for (const auto& entry: std::filesystem::directory_iterator(root)) {
CreateFileEntry(entry);
}
}
explicit FileDialogWindow(Widget* parent, const std::filesystem::path& root) : FileDialogWindow(root)
{
Parent(parent);
}
void SetSelectedFile(const std::string& filename);
void SetConfirmOverwrite(bool confirm);
void SetBlockingWhileOpen(bool blocking);
bool GetBlockingWhileOpen() const;
protected:
int directories_indexed = 0;
int index = 0;
VerticalListLayout* file_layout;
std::vector<Rect*> file_entry_widgets;
private:
};
class OpenFileDialogWindow : public JUI::Window { };
class SaveFileDialogWindow : public JUI::Window { };
class SelectFolderDialog : public JUI::Window { };
}

View File

@@ -0,0 +1,119 @@
#pragma once
#include <JUI/Widgets/Rect.hpp>
#include <JUI/Widgets/ImageRect.hpp>
#include <JUI/Widgets/FpsGraph.hpp>
#include "Window.hpp"
namespace JUI {
///
struct DataPoint {
Vector2 pos;
Color4 color;
};
///
class FpsGraph : public JUI::ImageRect
{
public:
/// Plots all stored data-points onto the render target.
void RenderDataPoints();
/// The default constructor initializes the basic layout of the FpsGraph class. Member variables are also zero-initialized.
FpsGraph();
/// Constructs an FpsGraph by explicitly specifying its parent element.
explicit FpsGraph(Widget* parent);
/// Inserts a new data point into the graph's data set.
void Plot(const Vector2& pt, const Color4& col);
void Plot(const DataPoint& data_pt);
/// Performs update logic on the state and appearance of this graph widget.
void Update(float delta) override;
/// A builtin preset for style and layout that appears docked at the bottom of the screen. It effectively behaves as an overlaid frame-graph.
/// TODO: Better name.
void SetupAsPseudoDockedElementAtBottomOfScreen();
void ShowTargetFPS(bool show);
bool ShowTargetFPS() const;
void TargetFPS(float fps);
float TargetFPS() const;
std::vector<DataPoint> data;
JGL::RenderTarget* canvas;
/// If the current FPS is lower than this value, use high_fps_color.
float HighFPSRange() const;
/// If the current FPS is lower than this value, and higher than SubparFPSRange(), use target_fps_color.
float TargetFPSRange() const;
/// If the current FPS is lower than this value, and higher than LowFPSRange(), use subpar_fps_color.
float SubparFPSRange() const;
/// If the current FPS is lower than this value, and higher than UnplayableFPSRange()
float LowFPSRange() const;
float UnplayableFPSRange() const;
/// Any FPS lower than this value will use slideshow_fps_color.
float SlideshowFPSRange() const;
protected:
float graph_height = 50;
float graph_width = 500;
float sample_history = 500;
float y_axis_scale = 600;
float target_fps = 60.f;
bool show_target_fps_bar = true;
Color4 target_fps_bar_color = Colors::White;
Color4 target_fps_label_color = Colors::Black;
Color4 high_fps_color = Colors::Blue;
Color4 target_fps_color = Colors::Green;
Color4 subpar_fps_color = Colors::Yellow;
Color4 low_fps_color = Colors::Red;
Color4 unplayable_fps_color = Colors::Purples::Magenta;
Color4 slideshow_fps_color = Colors::Black;
float high_fps_range_factor = 2.f;
float target_fps_range_factor = 1.f;
float subpar_fps_range_factor = 0.5f;
float low_fps_range_factor = 0.25f;
float unplayable_fps_range_factor = 0.125f;
float slideshow_fps_range_factor = 0.0625f;
private:
};
class FpsGraphWindow : public Window
{
public:
FpsGraphWindow();
explicit FpsGraphWindow(Widget* parent);
FpsGraph* GraphWidget() {return data_graph; }
void DockGraph() {
this->Close();
// TODO:: unsafe.
data_graph->Parent(this->GetParent());
data_graph->SetupAsPseudoDockedElementAtBottomOfScreen();
}
void UndockGraph() {
this->Open();
data_graph->Parent(this->Content());
}
protected:
FpsGraph* data_graph;
private:
};
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include "Rect.hpp"
#include "ImageRect.hpp"
namespace JUI {
/// A widget that plots and displays a data set in the form of various graphs.
class Graph : public ImageRect {
public:
/// The default constructor sets sensible defaults for style properties and zero-initializes other members.
Graph()
{
}
/// Constructs a Graph by explicitly specifying its parent element.
explicit Graph(Widget* parent) : Graph() {
this->Parent(parent);
}
protected:
private:
};
class PieChart : public Graph {
public:
protected:
private:
};
class LineGraph : public Graph {
public:
protected:
private:
};
}

View File

@@ -22,7 +22,7 @@ namespace JUI
{
public:
GridLayout() : LayoutContainer() {
Name("GridLayout");
}
explicit GridLayout(Widget* parent) : GridLayout()
{

View File

@@ -0,0 +1,37 @@
#pragma once
#include <JUI/Base/ImageBase.hpp>
#include <JUI/Widgets/Button.hpp>
namespace JUI {
class ImageButton;
}
class JUI::ImageButton : public ImageBase, public Button {
public:
ImageButton();
explicit ImageButton(Widget* parent);
~ImageButton() override = default;
void Update(float delta) override;
void Draw() override;
[[nodiscard]] Color4 HoveredImageColor() const;
[[nodiscard]] Color4 BaseImageColor() const;
[[nodiscard]] Color4 PressedImageColor() const;
[[nodiscard]] Color4 DisabledImageColor() const;
void HoveredImageColor(const Color4& value);
void BaseImageColor(const Color4& value);
void PressedImageColor(const Color4& value);
void DisabledImageColor(const Color4& value);
void ImageColors(const Color4& hover, const Color4& base, const Color4& press, const Color4& disabled);
protected:
void UpdateImageVisualState();
protected:
Color4 hover_img_color = Colors::White;
Color4 base_img_color = Colors::White;
Color4 pressed_img_color = Colors::White;
Color4 disabled_img_color = Colors::White;
};

View File

@@ -9,17 +9,23 @@
/// @desc A widget that contains and renders an image provided by JGL, within a rectangular frame.
/// @edit 2024-08-05
#pragma once
#include <JUI/Widgets/Rect.hpp>
#include <JUI/Base/ImageBase.hpp>
namespace JUI
{
class ImageRect: public Rect, public ImageBase
{
namespace JUI {
class ImageRect: public Rect, public ImageBase {
public:
ImageRect();
explicit ImageRect(Widget* parent);
void Update(float delta) override;
void Draw() override;
bool FitImageToBounds() const { return fit_image_to_bounds;}
void FitImageToBounds(bool value) { fit_image_to_bounds = value;}
protected:
bool fit_image_to_bounds;
};
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include <JUI/Base/Widget.hpp>
#include <JUI/Widgets/Rect.hpp>
#include <JUI/Widgets/Checkbox.hpp>
#include <JUI/Widgets/TextRect.hpp>
#include <JUI/Widgets/Text.hpp>
#include <JUI/Widgets/ListLayout.hpp>
namespace JUI
{
/// A composite class which combines a Checkbox and TextLabel into one element.
class LabeledCheckbox : public Rect
{
public:
LabeledCheckbox() {
auto* layout = new HorizontalListLayout(this);
}
explicit LabeledCheckbox(Widget* parent);
Checkbox* CheckboxInstance();
TextRect* LabelInstance();
Text* TextInstance();
};
}

View File

@@ -0,0 +1,19 @@
#include <JUI/Widgets/Slider.hpp>
#include <JUI/Base/TextBase.hpp>
namespace JUI {
/// A combination widget of a Slider and Text element, which is drawn over the slider elements.
class LabeledSlider : public Slider, public TextBase {
public:
LabeledSlider();
explicit LabeledSlider(Widget* parent);
void Draw() override;
void Update(float elapsed) override;
protected:
private:
};
}

View File

@@ -0,0 +1,113 @@
/// Josh's User Interface Library
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2025 Redacted Software
/// This work is dedicated to the public domain.
/// @file Link.hpp
/// @desc Special category of text-objects that model a clickable link in HTML.
/// @edit 2025-06-11
#pragma once
#include <JUI/Widgets/Text.hpp>
#include "JUI/Mixins/Clickable.hpp"
namespace JUI {
struct LinkInvokedEventArgs {};
/// Controls when Link widgets fire their Cicked callback. It can fire right when the widget is clicked, or when it is is released.
enum class LinkClickMode { Press, Release };
/// The Link widget is a specialization of the Text widget that has the appearance and behavior of a clickable link, such as in HTML.
class Link : public Text, public Clickable, public Hoverable {
public:
enum LinkClickMode ClickMode = LinkClickMode::Release;
Event<std::optional<LinkInvokedEventArgs>> Invoked;
/// This event is fired when the user clicks/releases the link.
Link() : Text(), Clickable(), Hoverable() {}
explicit Link(const std::string& content) : Link() {
this->Content(content);
}
explicit Link(Widget* parent) : Link() {
this->Parent(parent);
}
void OnHover(const Vector2 &MousePos) override {
TextColor(Colors::Blues::CornflowerBlue);
}
void OnExit(const Vector2 &MousePos) override {
TextColor(Colors::Blues::DeepSkyBlue);
}
void OnClick(const Vector2& mouse_pos, const MouseButton& btn) override {
if (disabled)
return;
Clickable::OnClick(mouse_pos, btn);
TextColor(Colors::White);
if (!fire_on_release) {
Clicked.Invoke(std::nullopt);
already_clicked = true;
}
}
void OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &btn,
bool still_hovering) override {
if (disabled)
return;
Clickable::OnRelease(mouse_pos, btn, still_hovering);
TextColor(Colors::Black);
if (fire_on_release) {
Clicked.Invoke(std::nullopt);
already_clicked = true;
}
}
void Update(float delta) override {
Text::Update(delta);
// TODO: Why does hovered not handle this?
Hoverable::Update(IsMouseInside(), delta);
// TODO: This is duplicated between here and Window.cpp
// Will be abstracted away into Clickable shortly.
// OMFG
if (IsHovered() && mb_state && !prev_mb_state)
{
OnClick(last_known_mouse_pos, mbtn);
}
if (IsClicked() && !mb_state)
{
OnRelease(last_known_mouse_pos, mbtn, IsHovered());
}
}
protected:
Color4 already_clicked_color;
Color4 hovered_color;
Color4 clicked_color;
bool disabled = false;
bool already_clicked = false;
bool fire_on_release;
private:
};
}

View File

@@ -1,8 +1,14 @@
//
// Created by dawsh on 11/24/24.
//
#pragma once
#ifndef LISTBOX_HPP
#define LISTBOX_HPP
#endif //LISTBOX_HPP
#include <JUI/Widgets/ScrollingRect.hpp>
namespace JUI {
class ListBox : public ScrollingRect
{
ListBox();
explicit ListBox(Widget* widget);
};
}

View File

@@ -31,30 +31,35 @@ namespace JUI
/// Lays child elements out in a vertical list.
/// Child element positions are overridden by this widget.
class VerticalListLayout : public ListLayout
{
class VerticalListLayout : public ListLayout {
public:
VerticalListLayout();
explicit VerticalListLayout(Widget* parent);
/// Computes the order and positioning of all child elements to display them in an ordered list.
void ApplyLayout() override;
void LayoutOrder(LayoutOrder::V order);
[[nodiscard]] LayoutOrder::V LayoutOrder() const;
/// @return the most-recently-computed sum of the heights of all child elements, with padding also taken into account.
[[nodiscard]] float CurrentContentHeight() const;
protected:
LayoutOrder::V layout = LayoutOrder::V::TOP;
float content_height = 0;
};
/// Lays child elements out in a horizontal list.
/// Child element positions are overridden by this widget.
class HorizontalListLayout : public ListLayout
{
class HorizontalListLayout : public ListLayout {
public:
HorizontalListLayout();
explicit HorizontalListLayout(Widget* parent);
void ApplyLayout() override;
void LayoutOrder(LayoutOrder::H order);
[[nodiscard]] LayoutOrder::H LayoutOrder() const;
[[nodiscard]] float CurrentContentWidth() const;
protected:
LayoutOrder::H layout = LayoutOrder::H::LEFT;
float content_width = 0;
};
}

View File

@@ -0,0 +1,83 @@
/// Josh's User Interface Library
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file NineSlice.hpp
/// @desc A widget that implements 9-slice scaling on an image.
/// @edit 2025-2-2
/// https://en.wikipedia.org/wiki/9-slice_scaling
#pragma once
#include <JUI/Base/Widget.hpp>
#include <JUI/Widgets/Rect.hpp>
#include <JUI/Base/ImageBase.hpp>
namespace JUI
{
class NineSliceRect : public Rect, public ImageBase {
public:
NineSliceRect();
explicit NineSliceRect(Widget* parent);
NineSliceRect(Widget* parent, JGL::Texture* texture);
/// Returns the bounds of the 'Top-Left' slice of the 9-slice.
[[nodiscard]] AABB2D TopLeftQuad() const;
/// Returns the bounds of the 'Top-Right' slice of the 9-slice.
[[nodiscard]] AABB2D TopRightQuad() const;
/// Returns the bounds of the 'Bottom-Left' slice of the 9-slice.
[[nodiscard]] AABB2D BottomLeftQuad() const;
/// Returns the bounds of the 'Bottom-Right' slice of the 9-slice.
[[nodiscard]] AABB2D BottomRightQuad() const;
/// Returns the bounds of the 'Top' slice of the 9-slice.
[[nodiscard]] AABB2D TopQuad() const;
/// Returns the bounds of the 'Left' slice of the 9-slice.
[[nodiscard]] AABB2D LeftQuad() const;
/// Returns the bounds of the 'Right' slice of the 9-slice.
[[nodiscard]] AABB2D RightQuad() const;
/// Returns the bounds of the 'Bottom' slice of the 9-slice.
[[nodiscard]] AABB2D BottomQuad() const;
/// Returns the bounds of the 'Center' slice of the 9-slice.
[[nodiscard]] AABB2D CenterQuad() const;
/// Sets the bounds of the quadrant for the 'Top-Left' slice of the 9-slice.
void TopLeftQuad(const AABB2D& quad);
/// Sets the bounds of the quadrant for the 'Top-Right' slice of the 9-slice.
void TopRightQuad(const AABB2D& quad);
/// Sets the bounds of the quadrant for the 'Bottom-Left' slice of the 9-slice.
void BottomLeftQuad(const AABB2D& quad);
/// Sets the bounds of the quadrant for the 'Bottom-Right' slice of the 9-slice.
void BottomRightQuad(const AABB2D& quad);
/// Sets the bounds of the quadrant for the 'Top' slice of the 9-slice.
void TopQuad(const AABB2D& quad);
/// Sets the bounds of the quadrant for the 'Right' slice of the 9-slice.
void RightQuad(const AABB2D& quad);
/// Sets the bounds of the quadrant for the 'Bottom' slice of the 9-slice.
void BottomQuad(const AABB2D& quad);
/// Sets the bounds of the quadrant for the 'Left' slice of the 9-slice.
void LeftQuad(const AABB2D& quad);
/// Sets the bounds of the quadrant for the 'Center' slice of the 9-slice.
void CenterQuad(const AABB2D& quad);
void Draw() override;
protected:
AABB2D top_left_quad;
AABB2D top_right_quad;
AABB2D bottom_left_quad;
AABB2D bottom_right_quad;
AABB2D top_quad;
AABB2D bottom_quad;
AABB2D right_quad;
AABB2D left_quad;
AABB2D center_quad;
};
}

View File

@@ -1,19 +0,0 @@
#pragma once
#include <JUI/Base/Widget.hpp>
#include <JUI/Widgets/Rect.hpp>
namespace JUI
{
class NineSliceImage
{
};
class NineSliceRect : public Rect, public NineSliceImage
{
};
}

View File

@@ -0,0 +1,40 @@
#pragma once
#include <JUI/Widgets/Rect.hpp>
#include <JUI/Widgets/RadioButton.hpp>
namespace JUI {
enum class RadioButtonSetMode { Exclusive, Multiple };
/// A rect widget that is specialized to support a set of radio buttons, where one or multiple can be selected at a time.
class RadioButtonSet : public Rect
{
public:
RadioButtonSet()
{
}
explicit RadioButtonSet(Widget* parent) : RadioButtonSet()
{
}
RadioButton* Add(const std::string& label)
{
}
protected:
// TODO: should likely be shared_ptr.
std::unordered_map<std::string, RadioButton*> btns;
};
}

View File

@@ -18,7 +18,7 @@
namespace JUI {
/// The Rect Widget Class.
/// Renders a rectangle or "box" and also serves as a vesatile container for laying out other widgets.
/// Renders a rectangle or "box" and also serves as a versatile container for laying out other widgets.
class Rect : public Widget, public RectBase {
public: /// Constructors
/// The default constructor.

View File

@@ -31,11 +31,11 @@
& &amp;
*/
namespace RichTextFormat
{
// TODO: Simpler "One-liner" Rich text, which is a horizontal sequence of textlabels automatically generated from a format.
class RichTextToken
{
namespace RichTextFormat {
class RichTextToken {
public:
std::string font_face_name;
std::string content;

View File

@@ -23,12 +23,18 @@ namespace JUI
Event<Vector2> MouseMoved;
void GlobalUIScale(const Vector2& value);
[[nodiscard]] Vector2 GlobalUIScale() const;
void Update(float delta) override;
[[nodiscard]] Vector2 GetAbsolutePosition() const override;
[[nodiscard]] Vector2 GetAbsoluteSize() const override;
bool ObserveMouseMovement(const Vector2 &latest_known_pos) override;
protected:
Vector2 ui_scale = Vector2(1, 1);
};
}

View File

@@ -10,60 +10,91 @@
/// @edit 2024-10-11
#pragma once
#include <JUI/Widgets/Rect.hpp>
namespace JUI {
class ScrollingRect;
}
namespace JUI
{
class ScrollingRect : public Rect
{
public:
ScrollingRect();
explicit ScrollingRect(Widget* parent);
// TODO: Fix direction of ScrollBar itself
// TODO: MouseWheel scroll, and ScrollBar clickable
// TODO: Clamp range of ScrollBar
JGL::RenderTarget* GetCanvas() { return canvas; }
/// A Rectangle Widget which has a larger renderable area than the visible area.
/// This allows user-controlled scrolling of the viewable content.
class JUI::ScrollingRect : public Rect {
public:
~ScrollingRect() override;
Vector2 CanvasSize() const;
void CanvasSize(const Vector2& canvas);
/// The default constructor initializes all child elements and members to reasonable defaults.
ScrollingRect();
Vector2 CanvasPosition() const
{
return {GetAbsolutePosition().x + scroll, GetAbsolutePosition().y};
}
Vector2 CanvasAbsolutePosition() const;
Vector2 CanvasAbsoluteSize() const;
float ScrollPos() const { return scroll;}
void ScrollPos(float pos) { scroll = pos;}
void Draw() override
{
J2D::Begin();
J2D::DrawRenderTarget(canvas, CanvasPosition());
J2D::End();
}
/// Constructs a ScrollingRect by specifying the parent widget to bind it to.
explicit ScrollingRect(Widget* parent);
public:
JGL::RenderTarget* GetCanvas();
[[nodiscard]] Vector2i CanvasSize() const;
[[nodiscard]] float ScrollPos() const { return scroll; }
[[nodiscard]] Vector2 CanvasPosition() const;
// TODO scrolling in either direction. Assuming vertical scroll for now.
Vector2 CanvasAbsolutePosition() const;
Vector2 CanvasAbsoluteSize() const;
public:
void ScrollPos(float pos) { scroll = pos; }
void CanvasSize(const Vector2i& new_size);
void RecomputeRenderTarget()
{
Rect::PreDraw();
InnerDraw();
Rect::PostDraw();
}
/// Sets the scroll amount of this ScrollingRect.
/// The value will be clamped between ScrollMinimum() and ScrollMaximum().
/// @param value The new scrolling amount to apply.
/// @see ScrollMinimum(), ScrollMaximum().
void SetScrollAmount(float value);
void InnerDraw() override
{
Rect::InnerDraw();
}
protected:
bool scrollbar_visible = true;
float scrollbar_width = 12;
Color4 scrollbar_color = Colors::Whites::Azure;
float scroll = 0;
Vector2 canvas_size {200, 200};
JGL::RenderTarget* canvas = nullptr;
private:
};
}
/// Scrolls this ScrollingRect by the specified amount.
/// @param amount The quantity of pixels by which to scroll the object.
/// @see SetScrollAmount()
float Scroll(float amount);
/// @return The amount of pixels this scrolling rect is currently scrolled by.
float GetScrollAmount() const;
[[nodiscard]] bool CanScroll() const;
/// The lower-bound of how far up the scroll can be.
[[nodiscard]] float ScrollMinimum() const;
/// The upper-bound of how far down the scroll can go.
[[nodiscard]] float ScrollMaximum() const;
void SetAutoAdjustScrollAmount(bool value);
bool GetAutoAdjustScrollAmount() const;
void ScrollToTop();
void ScrollToBottom();
// TODO: Figure out how to automatically adjust this as child widgets are added, removed, and changed in size.
float canvas_height = 0;
public:
void InnerDraw() override;
void Draw() override;
void Update(float delta) override;
bool ObserveKeyInput(Key key, bool pressed) override;
bool ObserveMouseWheel(int mwheel) override;
protected:
bool vertical_scrollbar_enabled = true;
bool horizontal_scrollbar_enabled = true;
bool scrollbar_visible = true;
float scrollbar_width = 12;
Color4 scrollbar_color = Colors::Whites::Azure;
float scroll = 0;
float scroll_amount = 10;
JGL::RenderTarget* canvas = nullptr;
bool auto_adjust_scroll_amount = false;
protected:
/* This isn't public because nothing should ever
* have to do this from the outside. -Redacted */
void RecomputeRenderTarget();
void DrawVerticalScrollbar();
};

View File

@@ -0,0 +1,89 @@
#pragma once
#include <JUI/Base/Widget.hpp>
#include <JUI/JUI.hpp>
namespace JUI {
/// The Separator Widget Class.
/// Fills space, and renders a single line through it, based on the given orientation.
/// Dashed, dotted, and solid lines of varying thickness are supported.
class Separator : public Widget {
public:
/// Constructs a Separator widget by specifying it's parent. Additional style parameters can also be specified.
/// @param parent
/// @param orientation
/// @param spacing
/// @param line_mode
/// @param line_color
/// @param line_thickness
Separator(Widget* parent,
const enum Orientation& orientation = Orientation::Horizontal,
const UDim& spacing = 5_px,
const enum LineFillMode& line_mode = LineFillMode::Solid,
const Color4& line_color = Colors::Black,
float line_thickness = 1.f
);
enum Orientation Orientation() const;
/// Sets whether the separator is laid out as a horizontal or vertical line.
/// Horizontal orientation draws a horizontal line, for separating elements in a vertical list.
/// Vertical orientation draws a vertical line, for separating elements in a horizontal list.
void Orient(const enum Orientation& orientation);
enum LineFillMode LineFillMode() const;
void LineFillMode(const enum LineFillMode& line_mode);
Color4 LineColor() const;
void LineColor(const Color4& color);
float GetAbsoluteSpacing() const;
void InnerDraw() override;
//float Spacing() const;
//void Spacing(float spacing);
/// @return The size of the separator object.
UDim Spacing() const { return spacing;}
/// Sets the size of the separator object.
void Spacing(const UDim& value) {
spacing = value;
UpdateSize();
}
/// @returns The spacing between
float LineSpacing() const { return line_spacing;}
void LineSpacing(float value) {
line_spacing = value;
}
protected:
/// The default constructor zero-initializes all members.
Separator();
/// Sets the widgets size based on the spacing and orientation.
void UpdateSize();
enum LineFillMode line_mode = LineFillMode::Solid;
enum Orientation orientation = Orientation::Horizontal;
Color4 line_color = Colors::Black;
float line_thickness = 1.f;
float line_spacing = 2.f;
UDim spacing = 2_px;
private:
};
}

View File

@@ -18,55 +18,88 @@
namespace JUI
{
template<typename T>
T roundMultiple( T value, T multiple )
{
if (multiple == 0) return value;
return static_cast<T>(std::round(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
}
/// A slider is a widget with a handle which can be pulled back and forth to change the value.
class Slider : public Rect, public Clickable, public Hoverable
{
public:
/// Invoked when the value of the slider is changed, usually by the user interacting with it.
Event<float> ValueChanged;
Slider() = default;
/// The default constructor initializes member variables to reasonable defaults.
Slider();
/// Constructs a slider by specifying it's parent widget.
explicit Slider(JUI::Widget* parent);
/// @return The minimum value allowed by the slider.
[[nodiscard]] float Minimum() const;
/// @return The maximum value allowed by the slider.
[[nodiscard]] float Maximum() const;
/// @return The increments the slider moves in.
[[nodiscard]] float Interval() const;
/// Returns the current stored value
[[nodiscard]] float CurrentValue() const;
[[nodiscard]] Color4 ScrubberColor() const;
[[nodiscard]] float ScrubberWidth() const;
[[nodiscard]] Color4 ScrubberColor() const;
/// @note Deprecated in favor of Slider::ScrubberSize().
[[deprecated]] [[nodiscard]] float ScrubberWidth() const;
[[nodiscard]] UDim2 ScrubberSize() const;
[[nodiscard]] float ScrubberRounding() const;
/// Returns whether the slider is currently being dragged by the user.
[[nodiscard]] bool Dragging() const;
[[nodiscard]] float Range() const;
/// Sets the minimum value allowed by the slider.
void Minimum(float min);
void Maximum(float max);
void Interval(float inter);
void CurrentValue(float value);
void ScrubberColor(const Color4& color);
void ScrubberWidth(float width);
/// @return The percentage of the slider, in the range [0, 1].
float Percentage() const;
/// Sets the percentage of the slider, in the range [0, 1]. The percentage is the underlying representation used interally.
void Percentage(float value);
void ScrubberColor(const Color4& color);
/// @note Deprecated in favor of Slider::ScrubberSize().
[[deprecated]] void ScrubberWidth(float width);
void ScrubberSize(const UDim2& size);
void ScrubberRounding(float rounding);
void SetDragging(bool value);
void OnClick(const J3ML::LinearAlgebra::Vector2 &MousePos, const JUI::MouseButton &MouseButton) override;
void OnRelease(const J3ML::LinearAlgebra::Vector2 &MousePos, const JUI::MouseButton &MouseButton, bool MouseStillOver) override;
void OnHover(const J3ML::LinearAlgebra::Vector2 &MousePos) override;
void OnExit(const J3ML::LinearAlgebra::Vector2 &MousePos) override;
void Update(float delta) override;
Vector2 GetScrubberAbsolutePosition() const;
Vector2 GetScrubberAbsoluteSize() const;
void Draw() override;
void InnerDraw() override;
protected:
float minimum = 0;
float maximum = 1;
float interval = 0.1;
float current;
bool dragging;
float scrubber_width = 20;
Color4 scrubber_color;
float percentage = 0.f;
bool dragging = false;
UDim2 scrubber_size {20_px, 100_percent};
Color4 scrubber_color = Colors::White;
float scrubber_rounding = 0.f;
private:
};
class VerticalSlider {
public:
protected:
private:
};
}

View File

@@ -0,0 +1,61 @@
/// Josh's User Interface Library
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2025 Redacted Software
/// This work is dedicated to the public domain.
/// @file Collapsible.cpp
/// @desc A rectangular widget with a
/// @edit 2025-02-14
#pragma once
#include <JUI/Widgets/Rect.hpp>
#include <JUI/Widgets/ListLayout.hpp>
#include <JUI/Widgets/TextButton.hpp>
// TODO: Each "Tab" needs its own content box.
namespace JUI {
class TabPageView : public Rect {
public:
/// Constructs
TabPageView()
{
}
explicit TabPageView(Widget* parent);
/// Adds a tab-page-set to the TabView by creating a default-style TextButton with the specified content.
std::pair<TextButton*, Rect*> AddTabPageSet(const std::string& content);
/// Adds a tab-page-set to the TabView by explicitly specifying the TextButton 'tab' and Rect 'page'.
Widget* AddTabPage(TextButton* tab_element);
int GetActiveTabIndex();
void SetActiveTabIndex(int index);
Rect* GetTabsContainer();
Rect* GetPagesContainer();
HorizontalListLayout* GetTabsListLayout();
TextButton* GetTabByIndex(int index);
Rect* GetPageByIndex(int index);
void NextTab();
void PrevTab();
void FirstTab();
void LastTab();
protected:
std::vector<TextButton*> tabs;
std::vector<Rect*> pages;
Rect* tabs_container;
Rect* page_container;
private:
};
}

View File

@@ -19,6 +19,7 @@ namespace JUI
Vector2 GetAbsoluteSize() const override;
Vector2 GetAbsolutePosition() const override;
void Draw() override;
void InnerDraw() override;
void Update(float delta) override;
protected:
private:

View File

@@ -9,15 +9,163 @@
/// @desc A box that accepts user keyboard input.
/// @edit 2024-08-02
#pragma once
#include <set>
#include <JUI/Mixins/Clickable.hpp>
#include "TextRect.hpp"
namespace JUI
{
class TextInputForm : public TextRect
{
namespace JUI {
// TODO: Text Selection
// TODO: Support Copy
// TODO: Support Paste
// TODO: Support Cut
// TODO: Support insert at cursor
// TODO: Simulate key repeat.
class TextInputForm : public TextRect, public Clickable {
public:
#pragma region Events
Event<> OnSelect;
Event<> OnDeselect;
Event<std::string> OnReturn;
#pragma endregion
public:
#pragma region Constructors
TextInputForm();
explicit TextInputForm(Widget* parent);
#pragma endregion
public:
#pragma region Methods
void Update(float elapsed) override;
void InnerDraw() override;
void Draw() override;
/// @return The number of characters the cursor is actually moved to the left by.
int MoveCursorLeft(int chars = 1);
/// @note The amount of
/// @return The number of characters the cursor is actually moved to the right by.
int MoveCursorRight(int chars = 1);
#pragma region Getters
#pragma endregion
#pragma region Setters
#pragma endregion
/// Returns the maximum position of the cursor, which is determined by the input buffer's length.
[[nodiscard]] unsigned int CursorMaxPosition() const;
void SetCursorPosition(unsigned int pos);
/// Fires the input event, clears the input buffer, and sets the state to be ready for further input.
void SendInput(bool clear_input);
/// Copy the contents of the input form, or the selection if active, into the the system's clipboard.
void Copy();
/// Paste the contents of the system's clipboard
void Paste();
void Cut();
void Backspace();
void Delete();
void PushStringToCurrentPlaceInInputBuffer(const std::string &snippet);
void PushKeyToCurrentPlaceInInputBuffer(const Key &key);
bool ObserveKeyInput(Key key, bool pressed) override;
void ShowNextHistory();
void ShowPrevHistory();
bool ObserveMouseInput(MouseButton btn, bool pressed) override;
bool ObserveMouseMovement(const Vector2 &latest_known_pos) override;
[[nodiscard]] std::string GetAutocompleteText() const;
void SetAutoCompleteText(const std::string& text);
[[nodiscard]] Color4 GetAutocompleteTextColor() const;
void SetAutocompleteTextColor(const Color4& color);
[[nodiscard]] bool HideAutocompleteOnSelect() const;
void SetHideAutocompleteOnSelect(bool hide);
[[nodiscard]] bool AutocompleteTextEnabled() const;
void SetAutocompleteTextEnabled(bool enabled);
[[nodiscard]] std::string InputBuffer() const;
void SetInputBuffer(const std::string& value);
void ClearInputBuffer();
[[nodiscard]] bool HasFocus() const;
void SetFocused(bool focused);
// TODO: Implement procedure to allow API consumer to validate the input, **before** clearing the buffer.
[[nodiscard]] bool ClearTextOnReturn() const;
void ClearTextOnReturn(bool value);
[[nodiscard]] bool DropFocusOnReturn() const;
void DropFocusOnReturn(bool value);
void GrabFocus();
void DropFocus();
[[nodiscard]] std::set<std::string> GetBlacklist() const;
void SetBlacklist(const std::set<std::string>& value);
void AddToBlacklist(const std::string& value);
// TODO: Implement selection of part of input text.
Color4 GetSelectionColor() const;
void SetSelectionColor(const Color4& color);
bool HasSelection() const;
std::string GetSelectedText() const;
[[nodiscard]] bool KeepInputHistory() const;
void KeepInputHistory(bool value);
std::string InputHistory(int index) const;
#pragma endregion
protected:
#pragma region Properties
bool keep_input_history = false;
bool clear_text_on_return = true;
bool drop_focus_on_return = false;
bool focused = false;
float cursor_blink_time = 0.f;
Color4 autocomplete_color = Style::InputForm::AutocompleteTextColor;
std::string autocomplete_text = "Hello World";
bool hide_autocomplete_on_select = true;
bool autocomplete_text_enabled = true;
std::set<std::string> blacklist;
bool selection_enabled;
#pragma endregion
#pragma region Working Variables
std::vector<std::string> history;
int history_index = -1;
bool selection_active;
int selection_start_index;
int selection_end_index;
unsigned int cursor_position = 0;
std::string input_buffer;
std::string saved_input_buffer;
/// Tracks the time (in seconds) since the TextInputForm was last opened.
/// @note This is used to circumvent a behavioral bug caused by how the input code is structured:
/// 1. User clicks a button that opens an InputForm.
/// 2. Grab InputForm Focus.
/// 3. User releases the button.
/// 4. The input form interprets this as "I am focused, but something else was just clicked".
/// 5. The input form drops its focus instantly, and it appears as if it was never focused.
float time_focused = 0.f;
#pragma endregion
private:
};
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include "Button.hpp"
namespace JUI {
/// The
class ToggleButton : public Button {
};
}

View File

@@ -2,24 +2,34 @@
#include <JUI/Widgets/TextRect.hpp>
#include "ReWindow/InputService.h"
namespace JUI
{
class Tooltip : public TextRect
{
public:
Tooltip() : TextRect()
{
Tooltip() : TextRect() {
Name("Tooltip");
}
explicit Tooltip(Widget* parent) : Tooltip()
{
this->Parent(parent);
explicit Tooltip(Widget* parent) : Tooltip() {
attachment = parent;
this->Parent(parent->GetFamilyTreeRoot());
this->AutoFitSizeToText(true);
this->ZIndex(10);
this->Visible(false);
this->Size({100_px, 20_px});
}
void Update(float delta) override;
~Tooltip() override {};
Widget* attachment = nullptr;
float PopupDelay() const { return 0;}
protected:
float popup_delay = 0.f;
};
}

View File

@@ -1,99 +1,54 @@
/// Josh's User Interface Library
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Widget.hpp
/// @desc Base Widget Class - All JUI widgets extend their behavior from this class.
/// @edit 2024-10-11
#pragma once
#include "ContextMenu.hpp"
namespace JUI
{
class ContextMenu : public Rect {
public:
ContextMenu() : Rect() {
this->BGColor(Colors::White);
this->Margin(2_px);
this->Size({200, 200, 0, 0});
layout = new VerticalListLayout(this);
}
explicit ContextMenu(Widget* parent) : ContextMenu()
{
this->Parent(parent);
}
class UtilityBar;
void SetFont(const JGL::Font& use_my_font)
{
font = use_my_font;
}
TextButton* AddItem(const std::string &name)
{
auto* line_item = new TextButton(layout);
line_item->SetFont(font);
line_item->SetContent(name);
line_item->SetTextSize(16);
line_item->SetTextColor(Colors::Black);
line_item->Size({0, 20, 1, 0});
return line_item;
}
protected:
VerticalListLayout* layout;
JGL::Font font;
private:
};
class UtilityBar : public Rect
/// A horizontal toolbar widget that is designed to be used at the top of windows, and subwindow widgets.
/// Convenience functions are provided for adding tool buttons and sub-menus.
/// @see class ContextMenu
class UtilityBar : public Rect, public NestableMenu, public TextStyler
{
public:
UtilityBar() : Rect()
{
// TODO: Make a note that all JGL::Font members on widgets need to be initialized to JGL::Fonts::Jupiteroid inside the constructor.
font = JGL::Fonts::Jupiteroid;
this->Size({0, 20, 1, 0});
this->Position({0,0,0,0});
this->BGColor(Colors::White);
this->BorderColor(Colors::Blues::CornflowerBlue);
this->SetBorderWidth(2);
this->Margin(2_px);
this->BorderMode(BorderMode::Outline);
layout = new HorizontalListLayout(this);
//layout->PaddingLeft(2_px);
layout->PaddingRight(2_px);
//layout->PaddingRight(2_px);
}
UtilityBar();
explicit UtilityBar(Widget* parent) : UtilityBar() {
this->Parent(parent);
}
explicit UtilityBar(Widget* parent);
TextButton* AddSubmenu(const std::string& name)
{
auto btn = AddButton(name);
/// Adds a new 'Submenu', which consists of a labeled button, which opens a contextual-submenu
ContextMenu* AddSubmenu(const std::string& name) override;
TextButton* AddButton(const std::string& name) override;
TextButton* AddButton(const std::string& name, const std::function<void()>& callback) override;
Separator* AddSeparator(const UDim& size = 5_px) override;
return btn;
}
std::vector<TextButton*> GetButtons() { return buttons;}
std::vector<ContextMenu*> GetSubmenus() { return submenus;}
void Font(const JGL::Font &value) override;
void TextSize(int value) override;
void TextColor(const Color4 &color) override;
TextButton* AddButton(const std::string& name)
{
auto str_width = font.MeasureString(name, 14);
auto* btn = new TextButton(layout);
btn->SetFont(font);
btn->SetTextSize(14);
btn->SetTextColor(Colors::Black);
btn->Size({static_cast<int>(str_width.x)+16, 0, 0, 1});
btn->SetBorderWidth(0.f);
btn->SetContent(name);
return btn;
}
void SetFont(const JGL::Font& use_my_font)
{
font = use_my_font;
}
protected:
HorizontalListLayout* layout;
JGL::Font font;
std::vector<TextButton*> buttons;
std::vector<ContextMenu*> submenus;
HorizontalListLayout* layout = nullptr;
private:
};
}
}

View File

@@ -23,23 +23,24 @@
#include <JUI/Mixins/Hoverable.hpp>
#include <JUI/Mixins/Clickable.hpp>
#include <JUI/Widgets/TextButton.hpp>
#include <JUI/Widgets/ImageButton.hpp>
namespace JUI
{
namespace JUI {
using J3ML::LinearAlgebra::Vector2;
class DockingStation {};
/// A container widget class, with title bar and buttons,
/// which can be dragged around, resized, and docked into other applicable widgets.
class Window : public Widget, public RectBase, public Clickable, public Hoverable, public Draggable, public Resizable, public Dockable
{
class Window : public Widget, public RectBase, public Clickable, public Hoverable, public Draggable, public Resizable, public Dockable {
public:
Event<> OnOpen;
Event<> OnClose;
/// The default constructor sets a default style for this Window.
Window();
/// Construct a window widget by specifying it's parent.
Window(Widget* parent);
explicit Window(Widget* parent);
/// Returns the current size (in x,y pixels) of the Window widget.
[[nodiscard]] Vector2 CurrentSize() const;
@@ -53,19 +54,50 @@ namespace JUI
/// Sets the maximum size (in x,y pixels) that the Window widget is allowed to be.
void MaxSize(const Vector2& constraint);
/// Returns the height (in pixels) of the Window's titlebar.
// TODO: Decide if this will auto-scale with the titlebar's text height, or the other way around.
int TitlebarHeight() const;
[[nodiscard]] int TitlebarHeight() const;
void HideTitlebar() {
titlebar_hidden = true;
Topbar->Visible(false);
}
void ShowTitlebar() {
titlebar_hidden = false;
Topbar->Visible(true);
}
/// @return True if this window widget is on-top of all other window-widgets with the same parent.
bool OnTop() const;
/// Brings this window widget to the top of the window-widget stack.
void BringToTop();
/// Moves this window up in the window-widget stack.
void MoveUp() {
zindex++;
}
/// Moves this window down in the window-widget stack.
void MoveDown() {
zindex--;
}
void TitlebarHeight(int height);
/// Returns the text displayed as the Window's title.
[[nodiscard]] std::string Title() const;
void SetTitle(const std::string& title);
void Title(const std::string& title);
/// Returns whether dragging the window about with the mouse is enabled.
/// @see class Draggable.
bool IsDraggable() const;
/// Sets the window to have the ability to be dragged around by the mouse.
void SetDraggable(bool value);
/// Returns whether this Window is able to be 'Docked' into another widget.
@@ -74,29 +106,40 @@ namespace JUI
void SetDockable(bool value);
/// Align topbar buttons to the left.
void LayoutControlsLeft();
/// Align topbar buttons to the right.
void LayoutControlsRight();
/// Returns whether resizing the window via right-click is enabled.
/// @see class Resizable
bool IsResizable() const;
void SetResizable(bool value);
void CornerRounding(float radius) override;
/// Returns a pointer to the Text Widget that is used to render the title bar's text.
Text* GetTitleInstance();
Text* TitleInstance();
/// Returns a pointer to the Rect Widget that is used to layout the title bar contents.
Rect* GetTopbarInstance();
Rect* TopbarInstance();
/// Returns a pointer to the Rect Widget that is used to layout the contents of the window.
Rect* GetViewportInstance();
Rect* ViewportInstance();
Rect* Content();
/// Returns a pointer to the Exit Button Widget.
TextButton* GetExitButtonInstance();
ImageButton* ExitButtonInstance();
Vector2 AbsoluteViewportPosition() const;
Vector2 AbsoluteViewportSize() const;
AABB2D AbsoluteViewportBounds() const;
/// Sets the font used by the title-bar text on this Window.
void SetTitleFont(const Font& f);
/// Toggles whether this window is actively being dragged by the mouse.
/// @see class Draggable.
void SetDrag(bool d) override;
/// @see class Widget.
void Update(float delta) override;
/// @see class Widget.
@@ -105,21 +148,80 @@ namespace JUI
void OnClick(const Vector2& m_pos, const MouseButton& m_btn) override;
/// @see class Clickable.
void OnRelease(const Vector2& m_pos, const MouseButton& m_btn, bool still_hovering) override;
/// @see class Hoverable. v
void OnHover(const J3ML::LinearAlgebra::Vector2 &MousePos) override
{
Hoverable::OnHover(MousePos);
zindex++;
focused = true;
this->BorderColor(Style::Window::OutlineColor);
this->BGColor(Style::Window::OutlineColor);
}
/// @see class Hoverable.
void OnExit(const J3ML::LinearAlgebra::Vector2 &MousePos) override {
Hoverable::OnExit(MousePos);
zindex--;
focused = false;
this->BorderColor(Style::Window::UnfocusedOutlineColor);
this->BGColor(Style::Window::UnfocusedOutlineColor);
}
/// @return whether this window will consume this observed mouse movement.
/// @note This is to prevent a user-mouse-action from being picked up by multiple widgets.
bool ObserveMouseMovement(const Vector2 &latest_known_pos) override;
bool ObserveMouseInput(JUI::MouseButton btn, bool pressed) override
{
// TODO: Consider how this plays with Clickable::OnClick and Clickable::OnRelease
if (visible) {
if (!pressed)
return Widget::ObserveMouseInput(btn, false);
if (this->focused)
return Widget::ObserveMouseInput(btn, pressed);
}
// Special case to allow drop of "Resizing"
if (btn == MouseButton::Right && pressed == false)
return Widget::ObserveMouseInput(btn, pressed);
return false;
}
[[nodiscard]] bool IsOpen() const;
[[nodiscard]] bool IsClosed() const;
virtual void Open();
virtual void Close();
virtual void SetOpen(bool value);
virtual void Toggle();
protected:
JUI::Rect* Topbar;
JUI::Rect* Viewport;
JUI::Text* TitleLabel;
JUI::TextButton* exit_btn;
JUI::TextButton* fs_btn;
void UpdateInternalWidgetsTitlebarHeight();
protected:
JUI::Rect* Topbar = nullptr;
JUI::Rect* Viewport = nullptr;
JUI::Text* TitleLabel = nullptr;
JUI::ImageButton* exit_btn = nullptr;
JUI::ImageButton* fs_btn = nullptr;
std::string title = "JUI Window";
bool resizable = true;
bool focused = false;
bool open = false;
//bool resizing = false;
bool draggable = true;
bool titlebar_hidden = false;
bool dockable = false;
int titlebar_height = 20;
int titlebar_height = Style::Window::TitlebarHeight;
int title_font_size = 16;
Vector2 max_size;
Vector2 min_size; //= {30, 30};
Vector2 max_size = {800, 600};
Vector2 min_size = {200, 100}; //= {30, 30};
UDim2 size_when_restart_began;
void DragTo(const Vector2 &pos);
};
}

838
main.cpp
View File

@@ -9,6 +9,10 @@
/// @desc Demo Program Entry Point
/// @edit 2024-07-11
/// This is a guided tour through creating, designing, and using a JUI scene.
#include <iostream>
#include <JGL/JGL.h>
#include <JUI/Widgets/Rect.hpp>
@@ -23,249 +27,588 @@
#include <JUI/Widgets/ScrollingRect.hpp>
#include <JUI/Widgets/UtilityBar.hpp>
#include <JUI/Widgets/Checkbox.hpp>
#include <rewindow/types/window.h>
#include <JUI/Widgets/TextInputForm.hpp>
#include <ReWindow/types/Window.h>
#include <ReWindow/Logger.h>
#include "JUI/Widgets/NineSlice.hpp"
#include <JUI/Widgets/Collapsible.hpp>
#include <JUI/Widgets/CommandLine.hpp>
JUI::Scene* scene;
JGL::Texture* sample_texture;
#include "JUI/Widgets/Anchor.hpp"
#include "JUI/Widgets/ColorPicker.hpp"
#include "JUI/Widgets/FileDialog.hpp"
#include "JUI/Widgets/FpsGraph.hpp"
#include "JUI/Widgets/LabeledSlider.hpp"
#include "JUI/Widgets/Link.hpp"
using namespace JUI;
float ui_scale = 1.f;
float accum = 0;
int iter = 0;
JUI::Scene* scene = nullptr;
JGL::Texture* sample_texture = nullptr;
JGL::Texture* slicer = nullptr;
JUI::VerticalListLayout* list = nullptr;
JUI::Window* scroller_window = nullptr;
JUI::ScrollingRect* scroller = nullptr;
JUI::TextRect* widget_count = nullptr;
JUI::CommandLine* console = nullptr;
JUI::Window* nineslice_demo_window = nullptr;
JUI::UtilityBar* topbar = nullptr;
JUI::FpsGraphWindow* fps_graph = nullptr;
/// Returns the sum total of widgets that are considered "descendnats" of the given widget.
/// A descendant is classified as being a child, or child of a child, and so on, of a given widget.
int count_descendants(JUI::Widget* w) {
return w->GetDescendants().size();
}
void CreateFileDialog() {
auto* dialog = new JUI::FileDialogWindow(scene, ".");
}
/// Creates the window that provides a "Welcome" dialog to the user.
/// Project metadata and copyright information is presented.
JUI::Window* CreateInfoboxWindow(JUI::Widget* root) {
auto* window = new JUI::Window(root);
auto* content_box = new JUI::Rect(window->Content());
content_box->Size({100_percent, 100_percent - 25_pixels});
auto* controls_box = new JUI::Rect(window->Content());
controls_box->Position({0_px, 100_percent - 25_pixels});
controls_box->Size({100_percent, 25_pixels});
auto* controls_layout = new JUI::HorizontalListLayout(controls_box);
auto make_dialog_btn = [controls_layout] (const std::string& label) {
auto* btn = new JUI::TextButton(controls_layout);
btn->Content(label);
//btn->Position({100_percent - 125_px, 100_percent - 20_px});
// TODO: AUTO SIZE - HORIZONTAL.
btn->Size({125_px, 20_px});
btn->TextColor(Colors::Black);
return btn;
};
auto* ok_btn = new JUI::TextButton(controls_layout);
ok_btn->Content("OK, Stop Yapping.");
ok_btn->Position({100_percent - 125_px, 100_percent - 20_px});
ok_btn->Size({125_px, 20_px});
ok_btn->TextColor(Colors::Black);
ok_btn->OnClickEvent += [window] (...) mutable {
window->Close();
};
return window;
}
/// Constructs, applies layout, and returns, a JUI::Window which demonstrates the NineSliceRect capability.
/// @param root The Widget to parent the NineSliceRect to.
JUI::Window* CreateNinesliceWindow(JUI::Widget* root) {
nineslice_demo_window = new JUI::Window(root);
nineslice_demo_window->Name("NineSlice Demo Window");
nineslice_demo_window->CornerRounding(10);
nineslice_demo_window->Size({50_percent, 50_percent});
nineslice_demo_window->Title("9-Slice Demo");
nineslice_demo_window->Close();
nineslice_demo_window->TitlebarHeight(12); {
auto* nineslice = new JUI::NineSliceRect(nineslice_demo_window);
nineslice->Content(slicer);
nineslice->Size({100_percent, 100_percent});
nineslice->BGColor(Colors::Transparent);
nineslice->TopLeftQuad({{0,0},{96,96}});
nineslice->TopRightQuad({{384-96,0},{96,96}});
nineslice->BottomLeftQuad({ {0, 378-96}, {96, 96}});
nineslice->BottomRightQuad({ {384-96, 378-96}, {96, 96}});
nineslice->TopQuad({ {96, 0}, {192, 96} });
nineslice->BottomQuad({ {96, 378-96}, {192, 96} });
nineslice->RightQuad({{384-(96), 96}, {96, 378-(96*2)}});
nineslice->LeftQuad({{0, 96}, {96, 378-(96*2)}});
nineslice->CenterQuad({{96, 96}, {384-(96*2), 378-(96*2)}});
auto* darkie = new JUI::Image(nineslice_demo_window->ViewportInstance(), sample_texture); {
darkie->FitImageToParent(true);
darkie->Color({255,255,255,128});
auto* t_list = new VerticalListLayout(nineslice_demo_window->ViewportInstance()); {
t_list->Padding(10_px);
TextRect* a = new TextRect(t_list); {
a->TextSize(16); a->Size({0, 20, 1, 0}); a->Center();
a->Content("This is a virtual window.");
}
TextRect* b = new TextRect(t_list); {
b->TextSize(16); b->Size({0, 20, 1, 0}); b->Center();
b->Content("You can drag it around via left-click, and resize via right-click. Isn't that cool??");
}
}
}
}
return nineslice_demo_window;
}
JUI::UtilityBar* CreateUtilityBar(JUI::Widget* root) {
auto* topbar = new UtilityBar(root); {
topbar->ZIndex(3);
// TODO: Make it so that when you mouse over another option in the *parent* menu, it closes any open submenus.
auto* demos = topbar->AddSubmenu("Demos");
{
demos->AddButton("9-Slice Widget Demo", [&] { nineslice_demo_window->Toggle(); });
demos->AddButton("Scroll Widget Demo", [&] {});
demos->AddButton("Command Line", [&] {
console->Toggle();
});
demos->AddButton("File Dialog", [&] {CreateFileDialog();});
auto* test_sub = demos->AddSubmenu("Fruit"); {
test_sub->AddButton("Apples");
test_sub->AddButton("Bananas");
test_sub->AddSeparator(20_px);
auto* berries = test_sub->AddSubmenu("Berries");
{
berries->AddButton("Grapes");
berries->AddButton("Strawberries");
berries->AddButton("Raspberries");
berries->AddButton("Blueberries");
}
}
auto* ptA = new JUI::Anchor(topbar, {30_px, 40_px});
auto* ptB = new JUI::Anchor(demos, UDim2::BottomRight);
auto* line = new JUI::ArrowDecorator(topbar, ptB, ptA);
}
auto* edit = topbar->AddButton("Edit");
auto* view = topbar->AddSubmenu("View"); {
view->AddButton("Increase UI Scale 5%", [&] {
ui_scale += 0.05f;
});
view->AddButton("Decrease UI Scale 5%", [&] {
ui_scale -= 0.05f;
});
view->AddSeparator();
view->AddButton("Toggle Docked FPSGraph", [&] mutable {
static bool docked = false;
if (docked) {
fps_graph->UndockGraph();
docked = false;
} else {
fps_graph->DockGraph();
docked = true;
}
});
}
auto* help = topbar->AddButton("About", [&] {
});
widget_count = new JUI::TextRect(topbar);
widget_count->Size(UDim2(100,20,0,0));
widget_count->AnchorPoint({1, 0});
widget_count->Position({100_percent, 0_percent});
widget_count->BGColor(Colors::Transparent);
widget_count->TextColor(Colors::Black);
widget_count->BorderColor(Colors::Transparent);
widget_count->BorderWidth(0);
widget_count->Center();
widget_count->AlignRight();
}
return topbar;
}
/// Constructs, styles, and returns a JUI::Rect which contains a sample of each widget in action.
JUI::Rect* CreateWidgetList(JUI::Widget* root) {
auto* window = new Window(root);
window->Title("Widgets");
window->Size({300_px, 80_percent});
auto* widget_list = new Rect(window->Content());
//column_rect->ZIndex(4);
widget_list->Size({0, 0, 1.f, 1.f});
widget_list->Position({0, 0, 0, 0});
widget_list->BGColor(Colors::Grays::Gainsboro);
auto* column_layout = new VerticalListLayout(widget_list);
auto* button_section = new Collapsible(column_layout);
{
button_section->Title("Button Styles");
button_section->Size({100_percent, 100_px});
auto* button_section_layout = new VerticalListLayout(button_section->ContentBox());
auto* btn_container1 = new Rect(button_section_layout);
{
btn_container1->Size({100_percent, 32_px});
auto* button = new TextButton(btn_container1);
//button->Position({5, 105, 0, 0});
button->Size({30_percent, 100_percent-10_px});
button->Position({10_px, 50_percent});
button->AnchorPoint({0, 0.5});
button->TextColor(Colors::Black);
button->Content("Left");
button->AlignLeft();
auto* tt2 = new JUI::Tooltip(button);
tt2->Content("Test 123");
auto* button2 = new TextButton(btn_container1);
button2->Size({30_percent, 100_percent-10_px});
button2->Position({50_percent, 50_percent});
button2->AnchorPoint({0.5f, .5f});
button2->TextColor(Colors::Black);
button2->Content("Center");
button2->AlignCenterHorizontally();
auto* button3 = new TextButton(btn_container1);
//button2->Position({5, 105, 0, 0});
button3->Size({30_percent, 100_percent-10_px});
button3->TextColor(Colors::Black);
button3->Position({100_percent-10_px, 50_percent});
button3->AnchorPoint({1, 0.5});
button3->Content("Right");
button3->AlignRight();
}
auto* btn_container2 = new Rect(button_section_layout);
{
btn_container2->Size({100_percent, 32_px});
btn_container2->BGColor(Colors::DarkGray);
auto* button4 = new TextButton(btn_container2);
//button->Position({5, 105, 0, 0});
button4->Size({30_percent, 100_percent-10_px});
button4->Position({10_px, 50_percent});
button4->AnchorPoint({0, 0.5});
button4->TextColor(Colors::Black);
button4->Content("Left");
button4->AlignLeft();
button4->CornerRounding(8);
button4->BorderWidth(2);
button4->BorderColor(Colors::Cyans::Aqua);
auto* button5 = new TextButton(btn_container2);
button5->Size({30_percent, 100_percent-10_px});
button5->Position({50_percent, 50_percent});
button5->AnchorPoint({0.5f, .5f});
button5->TextColor(Colors::Black);
button5->Content("Center");
button5->AlignCenterHorizontally();
button5->CornerRounding(8);
button5->BorderWidth(2);
button5->BorderColor(Colors::Cyans::Aqua);
auto* button6 = new TextButton(btn_container2);
button6->Size({30_percent, 100_percent-10_px});
button6->TextColor(Colors::Black);
button6->Position({100_percent-10_px, 50_percent});
button6->AnchorPoint({1, 0.5});
button6->Content("Right");
button6->AlignRight();
button6->CornerRounding(8);
button6->Disable();
}
auto* link_container = new Rect(button_section_layout);
{
link_container->Size({100_percent, 20_px});
link_container->BGColor(Colors::DarkGray);
auto* link = new JUI::Link(link_container);
link->Content("Clickable Link");
link->Center();
link->Clicked += [](auto event) {
std::cout << "Link Released" << std::endl;
};
}
}
auto* checkbox_section = new Collapsible(column_layout);
{
checkbox_section->Title("Checkbox Styles");
checkbox_section->Size({100_percent, 60_px});
auto* checkbox_layout = new VerticalListLayout(checkbox_section->ContentBox());
{auto* set_1 = new Rect(checkbox_layout);
set_1->Size({100_percent, 24_px});
auto* set_1_layout = new HorizontalListLayout(set_1);
auto* label = new TextRect(set_1_layout);
label->Content("Standard");
label->BorderWidth(0);
label->AutoFitSizeToText(true);
//label->Size({50_px, 20_px});
auto* separator_1 = new Separator(set_1_layout, Orientation::Vertical, 10_px);
separator_1->Visible(false);
auto* check1 = new Checkbox(set_1_layout);
check1->Size({20_px, 20_px});
auto* separator_2 = new Separator(set_1_layout, Orientation::Vertical, 10_px);
separator_2->Visible(false);
auto* check2 = new Checkbox(set_1_layout);
check2->Size({20_px, 20_px});
check2->CheckedColor(Colors::Blue);
check2->CornerRounding(7);
auto* separator_3 = new Separator(set_1_layout, Orientation::Vertical, 10_px);
separator_3->Visible(false);
auto* check3 = new Checkbox(set_1_layout);
check3->Size({20_px, 20_px});
check3->CheckedColor(Colors::Oranges::Coral);
check3->CornerRounding(7);
auto* separator_4 = new Separator(set_1_layout, Orientation::Vertical, 10_px);
separator_4->Visible(false);
auto* check4 = new Checkbox(set_1_layout);
check4->Size({20_px, 20_px});
check4->DisabledBorderColor(Colors::Black);
check4->Disable();
};
{auto* set_2 = new Rect(checkbox_layout);
set_2->Size({100_percent, 24_px});
auto* layout = new HorizontalListLayout(set_2);
auto* label = new TextRect(layout);
label->Content("Customized:");
label->BorderWidth(0);
label->AutoFitSizeToText(true);
auto* separator_1 = new Separator(layout, Orientation::Vertical, 20_px);
separator_1->Visible(false);
auto* check1 = new Checkbox(layout);
check1->Size({20_px, 20_px});
auto* separator_2 = new Separator(layout, Orientation::Vertical, 10_px);
separator_2->Visible(false);
auto* check2 = new Checkbox(layout);
check2->Size({20_px, 20_px});
check2->CheckedColor(Colors::Blue);
check2->CornerRounding(7);
auto* separator_3 = new Separator(layout, Orientation::Vertical, 10_px);
separator_3->Visible(false);
auto* check3 = new Checkbox(layout);
check3->Size({20_px, 20_px});
check3->CheckedColor(Colors::Oranges::Coral);
check3->CornerRounding(10);
check3->BorderMode(BorderMode::Inset);
check3->BorderWidth(2);
}
}
auto* inputform_section = new Collapsible(column_layout);
{
inputform_section->Title("Input Forms");
inputform_section->Size({100_percent, 50_px});
auto* input_form = new TextInputForm(inputform_section->ContentBox());
input_form->Size({0,24, 1, 0});
input_form->Content("");
input_form->SetAutoCompleteText("Accepts text input!");
input_form->TextSize(14);
}
auto* slider_section = new Collapsible(column_layout);
{
slider_section->Title("Slider Styles");
slider_section->Size({100_percent, 50_px});
auto* slider = new LabeledSlider(slider_section->ContentBox());
slider->Size({90_percent, 12_px});
slider->Position({50_percent, 50_percent});
slider->AnchorPoint({0.5f, 0.5f});
slider->Content("Value: 0");
slider->Minimum(-1.f);
slider->Maximum(1.f);
slider->CurrentValue(0.f);
slider->Interval(1/5.f);
slider->ValueChanged += [slider](float value) mutable {
slider->Content(std::format("Value: {}", value));
};
slider->CornerRounding(6);
slider->ScrubberRounding(12);
slider->ScrubberColor(Colors::Black);
slider->ScrubberSize({24_px, 24_px});
//slider->SetClipsDescendants(true);
slider->BGColor(Colors::Blues::DarkSlateBlue);
slider->Center();
}
auto* collapsible = new Collapsible(column_layout);
collapsible->Size({100_percent, 200_px});
auto* radio_btn_set = new Rect(collapsible->ContentBox());
radio_btn_set->Size({100_percent, 20_px});
auto* radio_btn_set_layout = new HorizontalListLayout(radio_btn_set);
radio_btn_set_layout->Padding(2_px);
auto* radio_a_btn = new Checkbox(radio_btn_set_layout);
radio_a_btn->Size({20_px, 20_px});
auto* radio_a_label = new TextRect(radio_btn_set_layout);
radio_a_label->BorderWidth(0);
radio_a_label->Size({20_px, 20_px});
radio_a_label->AutoFitSizeToText(true);
radio_a_label->Content("A ");
radio_a_label->TextSize(12);
auto* radio_b_btn = new Checkbox(radio_btn_set_layout);
radio_b_btn->Size({20_px, 20_px});
auto* radio_b_label = new TextRect(radio_btn_set_layout);
radio_b_label->BorderWidth(0);
radio_b_label->Size({20_px, 20_px});
radio_b_label->AutoFitSizeToText(true);
radio_b_label->Content("B ");
radio_b_label->TextSize(12);
auto* radio_c_btn = new Checkbox(radio_btn_set_layout);
radio_c_btn->Size({20_px, 20_px});
return widget_list;
}
JUI::Window* CreateScrollDemoWindow(Widget* root) {
auto* scroller_demo = new JUI::Window(root);
scroller_demo->Name("ScrollDemo Window");
scroller_demo->Position({10_percent, 10_percent});
scroller_demo->Size({30_percent, 25_percent});
scroller_demo->Title("ScrollingRect Demonstration");
/*Tween* t = scroller_demo->TweenPosition({50_percent, 50_percent}, {.time = 5});
t->Completed += [] () { std::cout << "Tween type test!!" << std::endl; };
scroller = new JUI::ScrollingRect(scroller_demo->Content());
scroller->Size({100_percent, 100_percent});
scroller->BGColor(Colors::Reds::LightCoral);
list = new JUI::VerticalListLayout(scroller);
list->LayoutOrder(JUI::LayoutOrder::V::BOTTOM);*/
return scroller_demo;
}
JUI::Window* CreateAnimationDemoWindow() {
return nullptr;
}
JUI::Scene* CreateScene() {
using namespace JUI;
Scene *root = new Scene();
auto *root = new Scene();
auto* topbar = new UtilityBar(root);
topbar->ZIndex(3);
auto* file = topbar->AddButton("File");
file->SetTooltip("This Dick Nigga", 0.5f);
fps_graph = new JUI::FpsGraphWindow(root);
file->OnClickEvent += [&, root, file] (Vector2 pos, JUI::MouseButton btn)
{
auto* ctx_menu = new ContextMenu(root);
ctx_menu->Position(UDim2(0,20,0,0));
ctx_menu->AddItem("A");
ctx_menu->AddItem("B");
ctx_menu->AddItem("c");
ctx_menu->ZIndex(3);
auto* colorpicker_wnd = new JUI::Window(root);
colorpicker_wnd->Name("Slider ColorPicker");
colorpicker_wnd->Size({150_px, 100_px});
colorpicker_wnd->MinSize({50, 50});
auto* cpicker = new JUI::ColorPicker(colorpicker_wnd->Content());
ctx_menu->MouseExit += [ctx_menu] (Vector2 _)
{
ctx_menu->Visible(false);
ctx_menu->Parent(nullptr);
// TODO: Collect
};
topbar = CreateUtilityBar(root);
};
nineslice_demo_window = CreateNinesliceWindow(root);
topbar->AddButton("Edit");
auto* view = topbar->AddButton("View");
topbar->AddButton("Help");
//auto* horizontal = new HorizontalListLayout(root);
//horizontal->ZIndex(1);
auto* sizer_1 = new Rect(root);
//sizer_1->ZIndex(4);
sizer_1->Size({0,-24,0.2f, 1.f});
sizer_1->Position({0, 24, 0, 0});
sizer_1->BGColor(Colors::Grays::Gainsboro);
auto* s1_vert = new VerticalListLayout(sizer_1);
auto* button = new TextButton(s1_vert);
//button->Position({5, 105, 0, 0});
button->Size({0, 35, 1, 0});
button->SetTextColor(Colors::Black);
button->SetContent("Button");
button->AlignLeft();
button->Padding(5_px);
auto* button2 = new TextButton(s1_vert);
//button2->Position({5, 105, 0, 0});
button2->Size({0, 35, 1, 0});
button2->SetTextColor(Colors::Black);
button2->SetContent("Button");
button2->AlignCenterHorizontally();
auto* button3 = new TextButton(s1_vert);
//button2->Position({5, 105, 0, 0});
button3->Size({0, 35, 1, 0});
button3->SetTextColor(Colors::Black);
button3->SetContent("Button");
button3->AlignRight();
auto* checkbox_container = new Rect(s1_vert);
checkbox_container->Size({0, 35, 1, 0});
// TODO: This instance will segfault on it's first call to be updated and I have **NO** idea why...
//scroller_window = CreateScrollDemoWindow(root);
auto* checkbox_horiz = new HorizontalListLayout(checkbox_container);
CreateInfoboxWindow(root);
auto* check1 = new Checkbox(checkbox_horiz);
console = new JUI::CommandLine(root); {
console->Close();
JUI::UILogs.OnLog += [&] (const std::string& msg, Color4 c){ console->Log(msg, c);};
}
//auto* main_wnd = new Window(root);
//auto* left = new HorizontalListLayout();
//auto* btn = new TextButton(root);
/*
auto* JUI = new TextRect(root);
JUI->SetClipsDescendants(true);
JUI->SetFont(FreeSans);
JUI->SetTextSize(48);
JUI->SetTextColor({32, 48, 192});
JUI->SetContent("Josh User Interface");
JUI->AlignBottom();
JUI->AlignCenterHorizontally();
JUI->AnchorPoint(Vector2(0.5f, 0.5f));
JUI->Position({50_percent, 50_percent});
//JUI->Size({30_percent, 10_percent});
JUI->FitText(true);
JUI->Padding(20_px);
JUI->BGColor({64, 64, 64, 128});
JUI->SetBorderWidth(2);
JUI->BorderColor({255, 255, 255, 128});
auto* Redacted = new TextRect(root);
Redacted->SetFont(FreeSans);
Redacted->SetTextSize(32);
Redacted->SetTextColor({255, 255, 255});
Redacted->SetContent("Redacted Software Group");
Redacted->Position({50_percent, 60_percent});
//Redacted->Size({30_percent, 10_percent});
Redacted->AlignCenterHorizontally();
Redacted->AlignTop();
Redacted->AnchorPoint(Vector2(0.5f, .5f));
Redacted->FitText(true);
Redacted->Padding(10_px);
Redacted->BGColor({32, 48, 192});
Redacted->SetBorderWidth(1);
Redacted->BorderColor({64, 64, 64});
//auto* scrollRect = new ScrollingRect(root);
// Rect //
Rect *rect_element = new Rect(root);
rect_element->Name("JimBob");
//Rect* element = new Rect(root);
//rect_element->Name("JimBob");
//element->BGColor({0,255,0});
rect_element->BGColor({0, 64, 0});
rect_element->Size({0, 0, 0.1f, 0.2f});
rect_element->SetClipsDescendants(true);
rect_element->BorderColor({255, 255, 255});
rect_element->SetBorderWidth(2.f);
rect_element->MouseEnter += [rect_element](auto coords) {
rect_element->BGColor({0, 128, 0});
};
rect_element->MouseExit += [rect_element](auto coords) {
rect_element->BGColor({0, 64, 0});
};
// End Rect //
Slider* slide = new Slider(root);
slide->Size({200, 40, 0, 0});
slide->BGColor({255, 255, 0});
slide->Position({0, 0, 0.125, 0.125});
// Button //
Button *button_element = new Button(root);
button_element->Name("BobJim");
button_element->BaseBGColor({64, 0, 64});
button_element->PressedBGColor({0, 64, 64});
button_element->Size({0, 0, 0.1f, 0.1f});
button_element->Position({0,0,0.25f,0.25f});
//button_element->SetClipsDescendants(true);
button_element->BaseBorderColor({255, 255, 255});
button_element->SetBorderWidth(2.f);
auto bpos = rect_element->Size();
//exit(1);
//button_element->Position(
// {bpos.X.Pixels, bpos.Y.Pixels, bpos.X.Scale - 0.2f, 0}); // I don't know how to use sx and sy - maxine
//button_element->OnToggleOnEvent += [rect_element] () {
// rect_element->BGColor({64, 0, 0});
//};
//button_element->OnToggleOffEvent += [rect_element] () {
// rect_element->BGColor({0, 64, 0});
//};
//button_element->OnToggleEvent += [button_element] () {
// Color4 incbg = button_element->BGColor();
// Once an overflow occurs it will reset anyway
// Thanks computer science
// if (incbg.b < 255)
// incbg.b += 10;
// button_element->BGColor(incbg);
//};
Text* btntext = new Text(button_element);
btntext->SetContent("I AM BUTTON");
btntext->SetFont(FreeSans);
btntext->SetTextSize(8);
btntext->SetTextColor({255, 0, 0});
// End Button //
*/
// Window //
auto* win_element = new JUI::Window(root);
win_element->CornerRounding(5);
win_element->Size({50_percent, 50_percent});
win_element->SetTitle("Window Widget");
//win_element->Padding(1_px);
// End Window //
auto darkie = new JUI::Image(win_element->GetViewportInstance(), sample_texture);
darkie->FitImageToParent(true);
darkie->Color({255,255,255,128});
auto list = new VerticalListLayout(win_element->GetViewportInstance());
list->Padding(10_px);
auto* widget_list = CreateWidgetList(root);
TextRect* a = new TextRect(list);
a->SetTextSize(16);
a->Size({0, 20, 1, 0});
//a->FitText(true);
a->Center();
a->SetContent("This is a virtual window.");
TextRect* b = new TextRect(list);
b->SetTextSize(16);
b->SetContent("You can drag it around via left-click, and resize via right-click.");
//b->FitText(true);
b->Size({0, 20, 1, 0});
b->Center();
root->SetViewportSize({800, 600});
return root;
}
class JUIDevelopmentTestWindow : public ReWindow::RWindow {
class JUIDevelopmentTestWindow : public ReWindow::OpenGLWindow {
public:
void initGL() {
bool result = JGL::Init(GetSize(), 0.f, 0.f);
JGL::Update(GetSize());
auto size = GetSize();
auto vec_size = Vector2i(size.x, size.y);
bool result = JGL::Init(vec_size, 0.f, 0.f);
JGL::Update(vec_size);
glClearColor(0.f, 0.f, 0.f, 0.f);
// TODO: Delete when we update to the next release of JGL
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // NOTE: This MUST be called for text rendering to work properly!!!
}
void Update()
void Update(float elapsed)
{
scene->Update(0.f);
widget_count->Content(std::format("Widgets: {}", count_descendants(scene)));
using namespace JUI::UDimLiterals;
accum += elapsed;
scene->Update(elapsed);
/// As a demo, change the scene's UI scale.
scene->GlobalUIScale({ui_scale, ui_scale});
if (accum > 1.f) {
iter--;
accum = 0.f;
/*auto* text = new JUI::TextRect(list);
text->Size({50_percent, 20_px});
text->ZIndex(iter);
text->LayoutOrder(-iter);
text->SetContent(std::format("{} Sampled Delta: {}ms", -iter, Math::Floor(elapsed*1000.f)));
scroller->canvas_height += text->GetAbsoluteSize().y;*/
}
}
void Draw()
@@ -273,17 +616,22 @@ public:
scene->Draw();
}
JUIDevelopmentTestWindow(const std::string& title, int w, int h) : ReWindow::RWindow(title, w, h) {}
JUIDevelopmentTestWindow(const std::string& title, int w, int h) : ReWindow::OpenGLWindow(title, w, h, 2, 1) {}
void OnRefresh(float elapsed) override {
Update();
JGL::Update(GetSize());
scene->SetViewportSize(GetSize());
Update(elapsed);
auto size = GetSize();
Vector2i vSize = Vector2i(size.x, size.y);
JGL::Update(vSize);
scene->SetViewportSize(Vector2(vSize));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
Draw();
this->GLSwapBuffers();
if (!focused)
std::this_thread::sleep_for(std::chrono::milliseconds(48));
this->SwapBuffers();
}
@@ -303,33 +651,51 @@ public:
}
void OnMouseMove(const ReWindow::MouseMoveEvent &) override
void OnMouseMove(const ReWindow::MouseMoveEvent &e) override
{
//JUI::UILogs.Log("Big Stepper");
Vector2 new_mouse_pos = Vector2(e.Position.x, e.Position.y);
if (scene->ObserveMouseMovement(new_mouse_pos))
return;
}
void OnKeyDown(const ReWindow::KeyDownEvent &) override
{
void OnKeyDown(const ReWindow::KeyDownEvent &) override {
}
//bool OnResizeRequest(const ReWindow::WindowResizeRequestEvent &e) override {}
JUIDevelopmentTestWindow() : ReWindow::RWindow() {}
void OnMouseWheel(const ReWindow::MouseWheelEvent &w) override {
ui_scale += w.WheelMovement * 0.125f;
if (scene->ObserveMouseWheel(w.WheelMovement))
return;
/// As a demo, change the scene's UI scale.
scene->GlobalUIScale({ui_scale, ui_scale});
}
};
#include <rewindow/logger/logger.h>
int main()
{
void inspect_widget(JUI::Widget* w, int depth = 1) {
std::cout << std::setw(depth*4);
std::cout << w->Name() << std::endl;
std::cout << std::setw(0);
depth++;
for (auto* child : w->GetChildren())
{
inspect_widget(child, depth);
}
}
int main() {
using namespace ReWindow;
// TODO: Find out new jlog api for silencing specific loggers.
ReWindow::Logger::Debug.EnableConsole(false);
auto* window = new JUIDevelopmentTestWindow("Test Window", 800, 600);
window->SetRenderer(RenderingAPI::OPENGL);
//window->SetRenderer(RenderingAPI::OPENGL);
window->Open();
window->initGL();
window->SetFullscreen(false);
@@ -339,20 +705,20 @@ int main()
JGL::Update({800, 600});
sample_texture = new JGL::Texture("assets/ld.png");
slicer = new JGL::Texture("assets/9slice.png");
scene = CreateScene();
inspect_widget(scene);
window->OnResizeRequestEvent += [&] (ReWindow::WindowResizeRequestEvent e){
Vector2 size = e.Size;//window->getLastKnownResize();
scene->SetViewportSize(size);
Vector2i size = Vector2i(e.Size.x, e.Size.y);//window->getLastKnownResize();
scene->SetViewportSize(Vector2(size));
std::cout << size.x << "," << size.y << std::endl;
JGL::Update(size);
};
window->OnMouseMoveEvent += [&] (MouseMoveEvent e)
{
scene->ObserveMouseMovement(e.Position);
};
window->OnMouseMoveEvent += [&] (MouseMoveEvent e) { };
window->OnMouseButtonUpEvent += [&] (MouseButtonUpEvent e) {
/// Invalid operands to binary expression 'MouseButton' and 'const MouseButton'
@@ -375,9 +741,13 @@ int main()
window->OnMouseButtonDownEvent += [&] (MouseButtonDownEvent e) {};
window->OnKeyDownEvent += [&] (KeyDownEvent e) {};
window->OnKeyDownEvent += [&] (KeyDownEvent e) {
scene->ObserveKeyInput(e.key, true);
};
window->OnKeyUpEvent += [&] (KeyUpEvent e) {};
window->OnKeyUpEvent += [&] (KeyUpEvent e) {
scene->ObserveKeyInput(e.key, false);
};
while (window->IsAlive()) {
//window->PollEvents();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 136 KiB

View File

@@ -1,15 +1,33 @@
#include <JUI/Base/ImageBase.hpp>
#include <JGL/JGL.h>
#include <JUI/JUI.hpp>
using namespace JGL;
namespace JUI
{
void ImageBase::Draw(const Vector2 &pos, const Vector2 &size) {
J2D::Begin();
void ImageBase::Draw(const Vector2 &pos) {
if (texture == nullptr) {
UILogs("Attempt to draw ImageBase that has nullptr texture!");
return;
}
// TODO: Support image rotation in the widget.
J2D::DrawSprite(*texture, pos, 0, origin, scale, image_color);
J2D::End();
}
void ImageBase::Draw(const Vector2 &pos, const Vector2 &size) {
if (texture == nullptr) {
UILogs("Attempt to draw ImageBase that has nullptr texture!");
return;
}
Vector2 overridden_scale = size / Vector2(texture->GetDimensions());
// TODO: Support image rotation in the widget.
J2D::DrawSprite(*texture, pos, 0, origin, overridden_scale, image_color);
}
ImageBase::ImageBase()
@@ -17,27 +35,38 @@ namespace JUI
this->texture = nullptr;
}
ImageBase::ImageBase(JGL::Texture* tex) : ImageBase()
{
this->texture = tex;
ImageBase::ImageBase(JGL::RenderTarget* content) : ImageBase() {
this->Content(content);
}
void ImageBase::Content(JGL::Texture *content) {
ImageBase::ImageBase(JGL::Texture* tex) : ImageBase()
{
this->Content(tex);
}
void ImageBase::Content(const JGL::Texture *content) {
this->texture = new JGL::RenderTarget(content);
}
void ImageBase::Content(JGL::RenderTarget *content) {
this->texture = content;
}
void ImageBase::Color(const Color4 &newColor) { this->image_color = newColor;}
void ImageBase::Scale(const Vector2 &newScale) { scale = newScale;}
void ImageBase::Origin(const Vector2 &newOrigin) { origin = newOrigin; }
JGL::Texture *ImageBase::Content() const { return texture;}
const JGL::Texture *ImageBase::Content() const { return texture->GetTexture();}
Color4 ImageBase::Color() const { return image_color;}
Vector2 ImageBase::Scale() const { return scale;}
Vector2 ImageBase::Origin() const { return origin;}
}

View File

@@ -4,66 +4,82 @@
using namespace JGL;
namespace JUI {
RectBase::RectBase() {}
RectBase::RectBase() = default;
bool RectBase::GetClipsDescendants() const { return clips_descendants;}
void RectBase::SetClipsDescendants(bool clipping) { clips_descendants = clipping;}
void RectBase::SetClipsDescendants(bool clipping) { clips_descendants = clipping; }
void RectBase::BGColor(const Color4 &col) { bg_color = col;}
void RectBase::BGColor(const Color4 &col) { bg_color = col; }
void RectBase::BorderColor(const Color4 &col) { border_color = col;}
void RectBase::BorderColor(const Color4 &col) { border_color = col; }
void RectBase::SetBorderWidth(float w) {border_width = w;}
void RectBase::BorderWidth(float w) { border_width = w; }
float RectBase::GetBorderWidth() const { return border_width;}
float RectBase::GetBorderWidth() const { return border_width; }
Color4 RectBase::GetBorderColor() const { return border_color;}
Color4 RectBase::GetBorderColor() const { return border_color; }
Color4 RectBase::BGColor() const { return bg_color; }
void RectBase::SetBorderStyling(const Color4 &color, float width) {
BorderColor(color);
SetBorderWidth(width);
BorderWidth(width);
}
void RectBase::Draw(const Vector2& abs_pos, const Vector2& abs_size)
{
void RectBase::Draw(const Vector2& abs_pos, const Vector2& abs_size) {
Draw(bg_color, border_color, abs_pos, abs_size);
}
void RectBase::Draw(const Color4& bgColor, const Color4& borderColor, const Vector2 &abs_pos, const Vector2 &abs_size) {
J2D::Begin();
RectBase::Draw(bgColor, borderColor, abs_pos, abs_size, corner_rounding_radius, border_width, border_mode, corner_mode);
// Background rect
if (corner_rounding_radius > 0)
J2D::FillRoundedRect(bgColor, abs_pos, abs_size, corner_rounding_radius);
else
J2D::FillRect(bgColor, abs_pos, abs_size);
}
void RectBase::DrawOutline(const Color4 &color, const Vector2 &pos, const Vector2 &size, float rounding,
float borderWidth, enum JUI::BorderMode borderMode, enum JUI::CornerRoundingMode cornerRoundingMode) {
// Outline rect - compute the size change to fit the border accurately.
Vector2 border_offset = {0, 0};
if (border_mode == BorderMode::Inset)
border_offset = {-border_width/2.f, -border_width/2.f};
if (border_mode == BorderMode::Middle)
/// Border is too small, don't draw it.
if (borderWidth <= 0) return;
if (borderMode == BorderMode::Inset)
border_offset = {-borderWidth/2.f, -borderWidth/2.f};
if (borderMode == BorderMode::Middle)
border_offset = {0, 0};
if (border_mode == BorderMode::Outline)
border_offset = {border_width/2.f, border_width/2.f};
if (borderMode == BorderMode::Outline)
border_offset = {borderWidth/2.f, borderWidth/2.f};
// Draw the outline.
if (border_width > 0)
{
if (corner_rounding_radius > 0)
J2D::OutlineRoundedRect(borderColor, abs_pos - border_offset, abs_size + (border_offset*2), corner_rounding_radius, border_width);
else
J2D::OutlineRect(borderColor, abs_pos - border_offset, abs_size + (border_offset*2), border_width);
}
if (cornerRoundingMode == CornerRoundingMode::None || rounding <= 0)
J2D::OutlineRect(color, pos - border_offset, size + (border_offset * 2), borderWidth);
else if (cornerRoundingMode == CornerRoundingMode::Rounded)
J2D::OutlineRoundedRect(color, pos - border_offset, size + (border_offset*2), rounding, borderWidth);
else if (cornerRoundingMode == CornerRoundingMode::Chamfer)
J2D::OutlineChamferRect(color, pos - border_offset, size + (border_offset*2), rounding, borderWidth);
}
void RectBase::DrawBG(const Color4 &color, const Vector2 &pos, const Vector2 &size, float rounding,
enum JUI::CornerRoundingMode rounding_mode) {
J2D::End();
if (rounding_mode == CornerRoundingMode::None || rounding <= 0)
J2D::FillRect(color, pos, size);
else if (rounding_mode == CornerRoundingMode::Rounded)
J2D::FillRoundedRect(color, pos, size, rounding);
else if (rounding_mode == CornerRoundingMode::Chamfer)
J2D::FillChamferRect(color, pos, size, rounding);
}
void RectBase::Draw(const Color4 &bgColor, const Color4 &fgColor, const Vector2 &pos, const Vector2 &size,
float rounding, float borderWidth, enum JUI::BorderMode borderMode,
enum JUI::CornerRoundingMode rounding_mode) {
DrawBG(bgColor, pos, size, rounding, rounding_mode);
DrawOutline(fgColor, pos, size, rounding, borderWidth, borderMode, rounding_mode);
}
void RectBase::BorderMode(const enum BorderMode &mode) {
@@ -76,6 +92,7 @@ namespace JUI {
corner_rounding_radius = radius;
}
float RectBase::CornerRounding() const { return corner_rounding_radius; }

View File

@@ -1,124 +1,139 @@
#include <JUI/Base/TextBase.hpp>
#include "JGL/JGL.h"
#include <JGL/JGL.h>
using namespace JUI;
namespace JUI {
void TextBase::SetContent(const std::string &content) { this->content = content; }
void TextBase::Content(const std::string& content) { this->content = content; state_redraw = true; }
void TextBase::SetTextColor(const Color4 &color) { this->text_color = color; }
void TextBase::OutlineColor(const Color4& color) { this->outline_color = color; }
void TextBase::SetOutlineColor(const Color4 &color) { this->outline_color = color; }
void TextBase::HorizontalTextAlign(const TextAlign::H& align) { this->h_align = align;}
void TextBase::SetHorizontalTextAlign(const TextAlign::H &align) { this->h_align = align;}
void TextBase::VerticalTextAlign(const TextAlign::V& align) { this->v_align = align; }
void TextBase::SetVerticalTextAlign(const TextAlign::V &align) { this->v_align = align; }
void TextBase::SetTextAlign(const TextAlign::H &h_align, const TextAlign::V &v_align) {
SetHorizontalTextAlign(h_align);
SetVerticalTextAlign(v_align);
}
void TextBase::AlignLeft() { SetHorizontalTextAlign(TextAlign::H::Left);}
void TextBase::AlignRight() { SetHorizontalTextAlign(TextAlign::H::Right);}
void TextBase::AlignTop() {SetVerticalTextAlign(TextAlign::V::Top);}
void TextBase::AlignBottom() {SetVerticalTextAlign(TextAlign::V::Bottom);}
void TextBase::AlignCenterBoth() {
SetVerticalTextAlign(TextAlign::V::Center);
SetHorizontalTextAlign(TextAlign::H::Center);
}
void TextBase::Center() { AlignCenterBoth(); }
void TextBase::AlignCenterHorizontally() {
SetHorizontalTextAlign(TextAlign::H::Center);
}
void TextBase::AlignCenterVertically() {
SetVerticalTextAlign(TextAlign::V::Center);
}
void TextBase::SetWordWrap(bool wrap) { word_wrap = wrap; }
void TextBase::Draw(const Vector2 &abs_pos, const Vector2 &abs_size) {
// Calculate how much to origin the text based on alignment.
float align_x = abs_pos.x;
float align_y = abs_pos.y;
auto bounds = this->GetFont().MeasureString(this->content, this->text_size);
if (h_align == TextAlign::H::Left) {
// Render from left-side of boundary.
align_x = abs_pos.x;
}
if (h_align == TextAlign::H::Center) {
// Render from horizontal center, origin by half of the text's width.
align_x = abs_pos.x + (abs_size.x / 2) - (bounds.x / 2);
}
if (h_align == TextAlign::H::Right) {
// Render from right-side of boundary, origin by text width.
align_x = abs_pos.x + abs_size.x - bounds.x;
}
if (v_align == TextAlign::V::Top) {
// Render from top of boundary.
align_y = abs_pos.y;
}
if (v_align == TextAlign::V::Center) {
// Render from vertical center, origin by half of the text's height.
align_y = abs_pos.y + (abs_size.y / 2) - (bounds.y / 2);
}
if (v_align == TextAlign::V::Bottom) {
// Render from bottom of boundary, origin by the text's height.
align_y = abs_pos.y + abs_size.y - bounds.y;
}
// Now we render.
float scale = 1.f;
JGL::J2D::Begin();
JGL::J2D::DrawString(
this->text_color,
this->content,
align_x, align_y,
scale,
this->text_size,
this->set_font);
JGL::J2D::End();
}
std::string TextBase::GetContent() const { return content;}
Color4 TextBase::GetTextColor() const { return text_color;}
Color4 TextBase::GetOutlineColor() const { return outline_color;}
TextAlign::H TextBase::GetHorizontalTextAlign() const { return h_align;}
TextAlign::V TextBase::GetVerticalTextAlign() const {return v_align;}
Vector2 TextBase::GetTextBounds() { return GetFont().MeasureString(content, text_size); }
JGL::Font TextBase::GetFont() const { return set_font;}
u32 TextBase::GetTextSize() const { return text_size; }
void TextBase::SetFont(const JGL::Font &font) {
set_font = font;
}
TextBase::TextBase() {
SetFont(JGL::Fonts::Jupiteroid);
}
void TextBase::SetTextSize(u32 size) {
text_size = size;
}
void TextBase::Update(float delta) {}
void TextBase::Align(const TextAlign::H& h_align, const TextAlign::V& v_align) {
HorizontalTextAlign(h_align);
VerticalTextAlign(v_align);
}
void TextBase::AlignLeft() { HorizontalTextAlign(TextAlign::H::Left); }
void TextBase::AlignRight() { HorizontalTextAlign(TextAlign::H::Right); }
void TextBase::AlignTop() { VerticalTextAlign(TextAlign::V::Top); }
void TextBase::AlignBottom() { VerticalTextAlign(TextAlign::V::Bottom); }
void TextBase::AlignCenterBoth() {
VerticalTextAlign(TextAlign::V::Center);
HorizontalTextAlign(TextAlign::H::Center);
}
void TextBase::Center() { AlignCenterBoth(); }
void TextBase::AlignCenterHorizontally() {
HorizontalTextAlign(TextAlign::H::Center);
}
void TextBase::AlignCenterVertically() {
VerticalTextAlign(TextAlign::V::Center);
}
void TextBase::WordWrap(bool wrap) { word_wrap = wrap; }
void TextBase::Draw(const Vector2& abs_pos, const Vector2& abs_size, const std::string& content, unsigned int size, const Color4& color) {
// Calculate how much to origin the text based on alignment.
float align_x = abs_pos.x;
float align_y = abs_pos.y;
std::string start = content;
std::vector<std::string> lines;
/*for (int i = 0; i < start.length(); i++) {
std::string substr = start.substr(0, i);
auto sub_bounds = this->GetFont().MeasureString(substr, size);
if (sub_bounds.x > abs_size.x)
}*/
auto bounds = TextStyler::Font().MeasureString(content, size);
// Do nothing if there is no text.
if (bounds.x == 0 || bounds.y == 0)
return;
// Render from left-side of boundary.
if (h_align == TextAlign::H::Left)
align_x = abs_pos.x;
// Render from horizontal center, origin by half of the text's width.
if (h_align == TextAlign::H::Center)
align_x = abs_pos.x + (abs_size.x / 2) - (bounds.x / 2);
// Render from right-side of boundary, origin by text width.
if (h_align == TextAlign::H::Right)
align_x = abs_pos.x + abs_size.x - bounds.x;
// Render from top of boundary.
if (v_align == TextAlign::V::Top)
align_y = abs_pos.y;
// Render from vertical center, origin by half of the text's height.
if (v_align == TextAlign::V::Center)
align_y = abs_pos.y + (abs_size.y / 2) - (bounds.y / 2);
// Render from bottom of boundary, origin by the text's height.
if (v_align == TextAlign::V::Bottom)
align_y = abs_pos.y + abs_size.y - bounds.y;
float scale = 1.f;
bool use_render_target = false;
// Has to be checked because this function lets you pass in parameters instead
// of using the ones we have saved. Which again, defeats the point. - Redacted.
if (content == this->content) {
// if Render Target needs updating.
if (state_redraw) {
text_canvas->Resize({(int) bounds.x, (int) bounds.y});
J2D::Begin(text_canvas, nullptr, true);
//J2D::FillRect(Colors::Red, {0,0}, bounds);
J2D::DrawString(color, content, 0, 0, size, scale, this->font);
J2D::End();
state_redraw = false;
}
use_render_target = true;
}
Vector2 text_pos = {align_x, align_y};
//J2D::Begin();
//J2D::OutlineRect(color, text_pos, bounds); // Draw bounding box for debugging.
use_render_target ? J2D::DrawRenderTarget(text_canvas, {align_x, align_y})
: J2D::DrawString(color, content, align_x, align_y, size, scale, this->font);
//J2D::End();
}
void TextBase::Draw(const Vector2& abs_pos, const Vector2& abs_size) {
Draw(abs_pos, abs_size, this->content, this->text_size, this->text_color);
}
std::string TextBase::Content() const { return content; }
Color4 TextBase::OutlineColor() const { return outline_color; }
TextAlign::H TextBase::HorizontalTextAlign() const { return h_align; }
TextAlign::V TextBase::VerticalTextAlign() const { return v_align; }
Vector2 TextBase::TextBounds() { return TextStyler::Font().MeasureString(content, text_size); }
void TextBase::Font(const JGL::Font& font) {
TextStyler::Font(font);
state_redraw = true;
}
void TextBase::TextSize(int size) {
TextStyler::TextSize(size);
state_redraw = true;
}
void TextBase::TextColor(const Color4& color) {
TextStyler::TextColor(color);
state_redraw = true;
}
void TextBase::Update(float delta) {}

File diff suppressed because it is too large Load Diff

4
src/JUI/DefaultStyle.cpp Normal file
View File

@@ -0,0 +1,4 @@
#include <JUI/DefaultStyle.hpp>
namespace DefaultStyle
{ }

View File

@@ -2,8 +2,45 @@
namespace JUI
{
// TODO: This is duplicated here and in ReCaveGame::Loggers.cpp
// Bring into jlog as utility functions.
std::string format_timestamp(jlog::Timestamp ts) {
return std::format(
"{}-{}-{} {}:{}:{}.{}",
ts.Year(),
ts.Month(),
ts.Day(),
ts.Hour().count(),
ts.Minute().count(),
ts.Second().count(),
ts.Millisecond().count());
}
// TODO: This is duplicated here and in ReCaveGame::Loggers.cpp
// Bring into jlog as utility functions.
std::string format_source_location(const std::source_location& location) {
return std::format("{} @ {}:{}", location.function_name(), location.file_name(), location.line());
}
JUILogger::JUILogger(const std::string &context, std::ofstream &file, Color4 contextColor, Color4 timestampColor,
Color4 locationColor, Color4 pointerColor, Color4 messageColor)
: jlog::GenericLogger(context, file, contextColor, timestampColor, locationColor, pointerColor, messageColor) { }
void JUILogger::Log(const std::string &message, const std::source_location &location, const jlog::Timestamp &ts) {
GenericLogger::Log(message, location, ts);
std::string formatted_timestamp = format_timestamp(ts);
std::string formatted_source_location = format_source_location(location);
std::string final_result = std::format("[{}] [{}] []", formatted_timestamp, this->ConsoleLogger::context, message);
OnLog.Invoke(final_result, messageColor);
}
std::ofstream GlobalLogFile("latest.log", std::ios_base::app);
jlog::GenericLogger UILogs {"JUI", GlobalLogFile, Colors::Green, Colors::Gray, Colors::Gray, Colors::Green, Colors::White};
JUILogger UILogs {"JUI", GlobalLogFile, Colors::Green, Colors::Gray, Colors::Gray, Colors::Green, Colors::White};
}

View File

@@ -1,7 +1,7 @@
#include <JUI/Mixins/Clickable.hpp>
void JUI::Clickable::Update(const Vector2 &mpos, const JUI::MouseButton &btn, bool hovering) {
//Hoverable::Update(mpos, delta);
}
void JUI::Clickable::OnRelease(const Vector2 &MousePos, const JUI::MouseButton &MouseButton, bool MouseStillOver) {

View File

@@ -0,0 +1,3 @@
#include <JUI/Mixins/Focusable.hpp>
bool JUI::Focusable::Focused() const { return focused; }

View File

@@ -7,12 +7,30 @@ void JUI::Hoverable::OnHover(const Vector2 &MousePos) {
void JUI::Hoverable::OnExit(const Vector2 &MousePos) {
OnExitEvent.Invoke(MousePos);
tooltip_threshold = 0;
//tooltip_threshold = 0;
}
void JUI::Hoverable::Update(const Vector2 &m_pos, float delta) {
if (tooltip != nullptr)
void JUI::Hoverable::OnHoverChildren(const Vector2& mouse) {
// TODO: Implement
}
void JUI::Hoverable::OnHoverDescendants(const Vector2& mouse) {
// TODO: Implement
}
void JUI::Hoverable::OnExitChildren(const Vector2& mouse) {
// TODO: Implement
}
void JUI::Hoverable::OnExitDescendants(const Vector2& mouse) {
// TODO: Implement
}
void JUI::Hoverable::Update(bool mouse_inside, float delta) {
hovered = mouse_inside;
/*if (tooltip != nullptr)
{
if (IsHovered()) {
tooltip->Visible(true);
@@ -20,16 +38,16 @@ void JUI::Hoverable::Update(const Vector2 &m_pos, float delta) {
} else {
tooltip->Visible(false);
}
}
}*/
if (IsHovered() && !hover_debounce) {
OnHover(m_pos);
OnHover({0,0});
hover_debounce = true;
}
if (!IsHovered() && hover_debounce) {
OnExit(m_pos);
OnExit({0,0});
hover_debounce = false;
}
}

View File

@@ -0,0 +1,21 @@
#include <JUI/Mixins/Pinnable.hpp>
namespace JUI {
void Pinnable::Pin() { SetPinned(true); }
void Pinnable::Unpin() { SetPinned(false); }
bool Pinnable::Pinned() const { return pinned; }
void Pinnable::OnPinStatusChanged(bool now_pinned) {
if (now_pinned)
OnPin();
else
OnUnpin();
}
void Pinnable::SetPinned(bool pinned) {
if (pinned != this->pinned) {
OnPinStatusChanged(pinned);
this->pinned = pinned;
}
}
}

View File

@@ -0,0 +1,25 @@
#include <JUI/Style/TextStyler.hpp>
JGL::Font JUI::TextStyler::Font() const { return font;}
int JUI::TextStyler::TextSize() const { return text_size; }
Color4 JUI::TextStyler::TextColor() const { return text_color; }
void JUI::TextStyler::Font(const JGL::Font &value) {
font = value;
}
void JUI::TextStyler::TextSize(int value) {
text_size = value;
}
void JUI::TextStyler::TextColor(const Color4 &color) {
text_color = color;
}
void JUI::TextStyler::Style(const JGL::Font &font, int size, const Color4 &color) {
Font(font);
TextSize(size);
TextColor(color);
}

202
src/JUI/Tween.cpp Normal file
View File

@@ -0,0 +1,202 @@
#include <JUI/Tween.hpp>
#include <J3ML/J3ML.hpp>
namespace JUI
{
using namespace J3ML;
float EasingFunctions::EaseInSine(float t) { return Math::Sin(Math::PiOverTwo * t); }
float EasingFunctions::EaseOutSine(float t) { return 1 + Math::Sin(Math::PiOverTwo * (--t)); }
float EasingFunctions::EaseInOutSine(float t) { return 0.5f * (1 + Math::Sin(Math::Pi * (t - 0.5f))); }
float EasingFunctions::EaseInQuad(float t) { return t * t;}
float EasingFunctions::EaseOutQuad(float t) { return t * (2.f - t);}
float EasingFunctions::EaseInOutQuad(float t) { return t < 0.5f ? 2.f * t * t : t * (4.f - 2.f * t) - 1; }
float EasingFunctions::EaseInCubic(float t) { return t * t * t;}
float EasingFunctions::EaseOutCubic(float t) { return 1.f + (--t) * t * t; }
float EasingFunctions::EaseInOutCubic(float t) { return t < 0.5f ? 4.f * t * t * t : 1 + (--t) * (2 * (--t)) * (2 * t); }
float EasingFunctions::EaseInQuart(float t) {
t *= t;
return t * t;
}
float EasingFunctions::EaseOutQuart(float t) {
t = (--t) * t;
return 1.f - t * t;
}
float EasingFunctions::EaseInOutQuart(float t) {
if (t < 0.5f) {
t *= t;
return 8 * t * t;
} else {
t = (--t) * t;
return 1 - 8 * t * t;
}
}
float EasingFunctions::EaseInQuint(float t) {
float t2 = t * t;
return t * t2 * t2;
}
float EasingFunctions::EaseOutQuint(float t) {
float t2 = (--t) * t;
return 1 + t * t2 * t2;
}
float EasingFunctions::EaseInOutQuint(float t) {
float t2;
if ( t < 0.5f) {
t2 = t * t;
return 16 * t * t2 * t2;
} else {
t2 = (--t) * t;
return 1 + 16 * t * t2 * t2;
}
}
float EasingFunctions::EaseInExpo(float t) {
return (Math::Pow(2, 8*t) - 1) / 255;
}
float EasingFunctions::EaseOutExpo(float t) {
return 1 - Math::Pow(2, -8*t);
}
float EasingFunctions::EaseInOutExpo(float t) {
if (t < 0.5f) {
return (Math::Pow(2, 16 * t) - 1) / 510;
} else {
return 1 - 0.5f * Math::Pow(2, -16 * (t - 0.5f));
}
}
float EasingFunctions::EaseInCirc(float t) {
return 1 - Math::Sqrt(1 - t);
}
float EasingFunctions::EaseOutCirc(float t) {
return Math::Sqrt(t);
}
float EasingFunctions::EaseInOutCirc(float t) {
if (t < 0.5f) {
return (1 - Math::Sqrt(1 - 2 * t)) * 0.5f;
} else {
return (1 + Math::Sqrt(2 * t - 1)) * 0.5f;
}
}
float EasingFunctions::EaseInBack(float t) { return t * t * (2.70158f * t - 1.70158f); }
float EasingFunctions::EaseOutBack(float t) { return 1 + (--t) * t * (2.70158 * t + 1.70158); }
float EasingFunctions::EaseInOutBack(float t) {
if (t < 0.5f) {
return t * t * (7 * t - 2.5f) * 2.f;
} else {
return 1 + (--t) * t * 2 * (7 * t + 2.5f);
}
}
float EasingFunctions::EaseInElastic(float t) {
float t2 = t * t;
return t2 * t2 * Math::Sin(t * Math::Pi * 4.5f);
}
float EasingFunctions::EaseOutElastic(float t) {
float t2 = (t - 1) * (t - 1);
return 1 - t2 * t2 * Math::Cos(t * Math::Pi * 4.5f);
}
float EasingFunctions::EaseInOutElastic(float t) {
float t2;
if (t < 0.45f) {
t2 = t * t;
return 8 * t2 * t2 * Math::Sin(t * Math::Pi * 9);
} else if (t < 0.55f) {
return 0.5f + 0.75f * Math::Sin(t * Math::Pi * 4);
} else {
t2 = (t - 1) * (t - 1);
return 1 - 8 * t2 * t2 * Math::Sin(t * Math::Pi * 9);
}
}
float EasingFunctions::EaseInBounce(float t) {
return Math::Pow(2, 6 * (t - 1)) * Math::Abs(Math::Sin(t * Math::Pi * 3.5f));
}
float EasingFunctions::EaseOutBounce(float t) {
return 1 - Math::Pow(2, -6 * t ) * Math::Abs(Math::Cos(t * Math::Pi * 3.5f));
}
float EasingFunctions::EaseInOutBounce(float t) {
if (t < 0.5f) {
return 8 * Math::Pow(2, 8 * (t - 1)) * Math::Abs(Math::Sin(t * Math::Pi));
} else {
return 1 - 8 * Math::Pow(2, -8 * t) * Math::Abs(Math::Sin(t * Math::Pi * 7));
}
}
float EasingFunctions::EaseInOutLinear(float t) { return t;}
void Tween::Update(float elapsed) {
if (time <= 0)
{
// TODO: Error in this case.
completed = true;
}
if (delay_time > 0)
{
delay_time -= elapsed;
return;
}
progress += (elapsed / time);
if (progress >= 1.f)
{
progress = 1.f;
if (!completed)
{
completed = true;
Completed.Invoke();
}
return;
}
float modified_progress = easing_func(progress);
tick_func(elapsed, modified_progress);
}
void Tween::ForceFinish() {
progress = 1.f;
}
void Tween::Cancel() {
completed = true;
}
bool Tween::Paused() const { return paused; }
bool Tween::HasCompleted() const { return completed; }
void Tween::Start() { progress = 0; Resume(); }
Tween::Tween(TweenTickFunc tick_func) {
this->tick_func = tick_func;
}
}

View File

@@ -16,6 +16,10 @@ JUI::UDim JUI::UDimLiterals::operator ""_px(unsigned long long px) {
return {static_cast<int>(px), 0.f};
}
JUI::UDim JUI::UDim::FromPixels(int pixels) { return {pixels, 0.f}; }
JUI::UDim JUI::UDim::FromScale(float scale) { return {0, scale}; }
JUI::UDim JUI::UDim::operator+(const UDim &rhs) const {
return {Pixels + rhs.Pixels, Scale + rhs.Scale};
}
@@ -31,3 +35,16 @@ JUI::UDim JUI::UDim::operator*(float rhs) const {
JUI::UDim JUI::UDim::operator/(float rhs) const {
return {static_cast<int>(Pixels / rhs), Scale/rhs};
}
JUI::UDim JUI::UDim::Lerp(const JUI::UDim &goal, float t) {
float scale = J3ML::Math::Lerp(Scale, goal.Scale, t);
float pixels = J3ML::Math::Lerp(Pixels, goal.Pixels, t);
return UDim(pixels, scale);
}
bool JUI::UDim::Equals(const JUI::UDim &rhs, float epsilon) {
return J3ML::Math::Equal((float)Pixels, rhs.Pixels, epsilon)
&& J3ML::Math::Equal(Scale, rhs.Scale, epsilon);
}

View File

@@ -1,5 +1,18 @@
#include <JUI/UDim2.hpp>
const JUI::UDim2 JUI::UDim2::Center {50_percent, 50_percent};
const JUI::UDim2 JUI::UDim2::TopLeft {0_percent, 0_percent};
const JUI::UDim2 JUI::UDim2::BottomRight {100_percent, 100_percent};
const JUI::UDim2 JUI::UDim2::BottomLeft {0_percent, 100_percent};
const JUI::UDim2 JUI::UDim2::TopRight {100_percent, 0_percent};
const JUI::UDim2 JUI::UDim2::TopCenter {50_percent, 0_percent};
const JUI::UDim2 JUI::UDim2::LeftCenter {0_percent, 50_percent};
const JUI::UDim2 JUI::UDim2::BottomCenter {50_percent, 100_percent};
const JUI::UDim2 JUI::UDim2::RightCenter {100_percent, 50_percent};
JUI::UDim2::UDim2() {
//X = {0, 0.f};
//Y = {0, 0.f};
@@ -47,3 +60,15 @@ Vector2 JUI::UDim2::GetScale() const {
Vector2 JUI::UDim2::GetPixels() const {
return {static_cast<float>(X.Pixels), static_cast<float>(Y.Pixels)};
}
JUI::UDim2 JUI::UDim2::Lerp(const JUI::UDim2 &goal, float t) {
return UDim2(
X.Lerp(goal.X, t),
Y.Lerp(goal.Y, t)
);
}
bool JUI::UDim2::Equals(const JUI::UDim2 &rhs, float epsilon) {
return X.Equals(rhs.X, epsilon) &&
Y.Equals(rhs.Y, epsilon);
}

View File

@@ -2,9 +2,9 @@
#include <JUI/Widgets/Button.hpp>
#include <jlog/Logger.hpp>
namespace JUI
{
namespace JUI {
Button::Button(): Rect(), Clickable() {
Name("Button");
BGColor(BaseBGColor());
BorderColor(BaseBorderColor());
}
@@ -13,15 +13,11 @@ namespace JUI
this->Parent(parent);
};
void Button::OnClick(const Vector2& mouse_pos, const MouseButton& btn)
{
void Button::OnClick(const Vector2& mouse_pos, const MouseButton& btn) {
if (disabled)
return;
Clickable::OnClick(mouse_pos, btn);
//BGColor(PressedBGColor());
//BorderColor(PressedBorderColor());
}
void Button::OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &btn,
@@ -31,14 +27,9 @@ namespace JUI
return;
Clickable::OnRelease(mouse_pos, btn, still_hovering);
//BGColor(BaseBGColor());
//BorderColor(BaseBorderColor());
}
void Button::UpdateVisualState()
{
void Button::UpdateVisualState() {
if (Disabled()) {
BGColor(DisabledBGColor());
BorderColor(DisabledBorderColor());
@@ -55,9 +46,8 @@ namespace JUI
}
void Button::Update(float delta) {
hovered = IsMouseInside();
Hoverable::Update(last_known_mouse_pos, delta);
Hoverable::Update(IsMouseInside(), delta);
// TODO: This is duplicated between here and Window.cpp
// Will be abstracted away into Clickable shortly.
@@ -74,6 +64,8 @@ namespace JUI
UpdateVisualState();
prev_mb_state = mb_state;
Rect::Update(delta);
}
void Button::Enable() {
@@ -115,8 +107,6 @@ namespace JUI
if (Disabled() || IsClicked())
return;
//BGColor(HoveredBGColor());
//BorderColor(HoveredBorderColor());
}
void Button::OnExit(const Vector2 &MousePos) {
@@ -124,9 +114,6 @@ namespace JUI
if (Disabled() || IsClicked())
return;
//BGColor(BaseBGColor());
//BorderColor(BaseBorderColor());
}
Color4 Button::HoveredBGColor() const { return hover_bg; }
@@ -166,7 +153,6 @@ namespace JUI
HoveredBorderColor(hover);
PressedBorderColor(pressed);
DisabledBGColor(disabled);
UpdateVisualState();
}
@@ -175,19 +161,6 @@ namespace JUI
HoveredBGColor(hover);
PressedBGColor(pressed);
DisabledBGColor(disabled);
UpdateVisualState();
}
void Button::SetTooltip(const std::string &content, float delay) {
tooltip_limit = delay;
// TODO: Verify this is okay to do:
tooltip = new Tooltip(this);
tooltip->SetContent(content);
tooltip->ZIndex(5);
tooltip->Visible(false);
}
}

View File

@@ -1,2 +0,0 @@
#include <JUI/Widgets/Canvas.hpp>

View File

@@ -1,8 +1,35 @@
#include <JUI/Widgets/Checkbox.hpp>
namespace JUI
{
namespace JUI {
Checkbox::Checkbox() : Button(), CheckboxBase() {
Padding(2_px);
}
Checkbox::Checkbox(Widget *parent) : Checkbox() {
this->Parent(parent);
}
void Checkbox::Update(float delta) {
Button::Update(delta);
}
void Checkbox::OnRelease(const Vector2 &mouse_pos, const MouseButton &bnt, bool still_hovering) {
Button::OnRelease(mouse_pos, bnt, still_hovering);
checked = !checked;
}
void Checkbox::InnerDraw() {
Rect::InnerDraw();
// TODO: Renders too large with BorderMode::Inset
if (checked) {
Vector2 pos_pad = ComputeElementPadding(GetAbsoluteSize(), {PaddingLeft(), PaddingTop()});
Vector2 size_pad = ComputeElementPadding(GetAbsoluteSize(), {PaddingLeft()+PaddingRight(), PaddingTop()+PaddingBottom()});
Vector2 padded_pos = GetAbsolutePosition() + pos_pad;
Vector2 padded_size = GetAbsoluteSize() - size_pad;
RectBase::Draw(check_color, border_color, padded_pos, padded_size);
}
}
}

View File

@@ -0,0 +1,84 @@
#include <JUI/Widgets/Collapsible.hpp>
namespace JUI {
Collapsible::Collapsible() : Rect() {
Name("Collapsible");
header = new TextButton(this);
header->Size({100_percent, header_height});
header->Content("Collapsible");
header->BaseBGColor(Colors::DarkGray);
header->BGColor(Colors::DarkGray);
header->Center();
//header->
header->OnClickEvent += [this] (auto a, auto b) {
if (Collapsed())
Expand();
else
Collapse();
};
lil_arrow = new TextRect(header);
lil_arrow->Size({header_height, header_height});
lil_arrow->BGColor(Colors::Transparent);
lil_arrow->Content("/\\");
lil_arrow->BorderWidth(0);
content_box = new Rect(this);
content_box->BGColor(Colors::Transparent);
content_box->BorderColor(Colors::Transparent);
content_box->BorderWidth(0);
content_box->Position({0_px, header_height});
content_box->Size({100_percent, 100_percent - header_height});
}
void Collapsible::Collapse() {
//Size(saved_size);
lil_arrow->Content("\\/");
content_box->Visible(false);
if (resize_anim != nullptr && !resize_anim->HasCompleted()) {
resize_anim->Cancel();
} else {
saved_size = Size();
}
resize_anim = TweenSize(UDim2(saved_size.X, header_height), {
.time = 0.25f
});
collapsed = true;
}
void Collapsible::Expand() {
lil_arrow->Content("/\\");
content_box->Visible(true);
if (resize_anim != nullptr && !resize_anim->HasCompleted())
resize_anim->Cancel();
resize_anim = TweenSize(saved_size, {
.time = 0.25f
});
collapsed = false;
}
bool Collapsible::Collapsed() const { return collapsed; }
Collapsible::Collapsible(Widget *parent) : Collapsible() {
this->Parent(parent);
}
TextButton *Collapsible::Header() { return header; }
Rect *Collapsible::ContentBox() {return content_box; }
UDim Collapsible::HeaderHeight() const { return header_height;}
void Collapsible::HeaderHeight(const UDim &value) { header_height = value; }
void Collapsible::Title(const std::string &value) {
header->Content(value);
}
std::string Collapsible::Title() const { return header->Content(); }
}

View File

@@ -0,0 +1,134 @@
#include <JUI/Widgets/ColorPicker.hpp>
std::string rgb2hex(int r, int g, int b, bool with_head) {
std::stringstream ss;
if (with_head)
ss << "#";
ss << std::hex << (r << 16 | g << 8 | b );
return ss.str();
}
void JUI::ColorPicker::Font(const JGL::Font &value) {
TextStyler::Font(value);
hue_label->Font(value);
sat_label->Font(value);
bri_label->Font(value);
hex_label->Font(value);
}
void JUI::ColorPicker::TextSize(int size) {
TextStyler::TextSize(size);
hue_label->TextSize(size);
sat_label->TextSize(size);
bri_label->TextSize(size);
hex_label->TextSize(size);
}
void JUI::ColorPicker::TextColor(const Color4 &color) {
TextStyler::TextColor(color);
hue_label->TextColor(color);
sat_label->TextColor(color);
bri_label->TextColor(color);
hex_label->TextColor(color);
}
void JUI::ColorPicker::StyleSlider(Slider *slider) {
slider->ScrubberSize({16_px, 16_px});
slider->CornerRounding(8);
slider->BGColor(Colors::DarkGray);
slider->ScrubberRounding(8);
slider->Size({100_percent-10_px, 16_px});
}
JUI::ColorPicker::ColorPicker(): Rect() {
Name("ColorPicker");
Size({100_percent, 100_percent});
//auto* row_layout = new JUI::VerticalListLayout(this);
hue_slider = new JUI::Slider(this);
StyleSlider(hue_slider);
hue_slider->Position({50_percent, 4_px});
hue_slider->AnchorPoint({0.5f, 0.f});
hue_slider->Minimum(0.f);
hue_slider->Maximum(360);
hue_slider->Interval(1.f / 360.f);
hue_slider->ValueChanged += [this] (float val) mutable {
hue_label->Content(std::format("Hue: {}", Math::FloorInt(val)));
hue = val;
hue_slider->ScrubberColor(Color4::FromHSV(hue, 1.f, 1.f));
OnColorValueChanged.Invoke(Color4::FromHSV(hue, sat, bri));
RecomputeVisuals();
};
hue_label = new JUI::Text(hue_slider);
hue_label->AlignCenterHorizontally();
hue_label->AlignTop();
hue_label->TextColor(Colors::White);
hue_label->Content("Hue: 0");
sat_slider = new JUI::Slider(this);
StyleSlider(sat_slider);
sat_slider->Position({50_percent, 24_px});
sat_slider->AnchorPoint({0.5f, 0.f});
sat_slider->Minimum(0);
sat_slider->Maximum(1);
sat_slider->Interval(1e-3f);
sat_slider->ValueChanged += [this] (float val) mutable {
sat_label->Content(std::format("Saturation: {}%", Math::Floor(val*100.f)));
sat = val;
OnColorValueChanged.Invoke(Color4::FromHSV(hue, sat, bri));
RecomputeVisuals();
};
sat_label = new JUI::Text(sat_slider);
sat_label->AlignCenterHorizontally();
sat_label->AlignTop();
sat_label->TextColor(Colors::White);
sat_label->Content("Saturation: 100%");
bri_slider = new JUI::Slider(this);
StyleSlider(bri_slider);
bri_slider->Position({50_percent, 44_px});
bri_slider->AnchorPoint({0.5f, 0.f});
bri_slider->Minimum(0);
bri_slider->Maximum(1);
bri_slider->Interval(1e-3f);
bri_slider->ValueChanged += [this] (float val) mutable {
bri_label->Content(std::format("Brightness: {}%", Math::Floor(val*100.f)));
bri = val;
OnColorValueChanged.Invoke(Color4::FromHSV(hue, sat, bri));
RecomputeVisuals();
};
bri_label = new JUI::Text(bri_slider);
bri_label->AlignCenterHorizontally();
bri_label->AlignTop();
bri_label->TextColor(Colors::White);
bri_label->Content("Brightness: 100%");
//auto* hue_box = new JUI::Rect();
hex_label = new JUI::TextRect(this);
hex_label->AlignCenterHorizontally();
hex_label->Size({100_percent, 24_px});
hex_label->Position({0_px, 100_percent});
hex_label->Content("#FFZZGG");
}
JUI::ColorPicker::ColorPicker(Widget *parent): ColorPicker() {
Parent(parent);
}
void JUI::ColorPicker::RecomputeVisuals() {
Color4 computed = Color4::FromHSV(hue, sat, bri);
Color4 inverse = Color4(255 - computed.r, 255 - computed.g, 255 - computed.b);
hex_label->TextColor(inverse);
hex_label->BGColor(computed);
hex_label->Content(std::format("{}", rgb2hex(computed.r, computed.g, computed.b, true)));
}

View File

@@ -0,0 +1,83 @@
#include <JUI/Widgets/CommandLine.hpp>
namespace JUI
{
// Get current date/time, format is YYYY-MM-DD.HH:mm:ss
const std::string currentDateTime() {
time_t now = time(0);
struct tm tstruct;
char buf[80];
tstruct = *localtime(&now);
// Visit http://en.cppreference.com/w/cpp/chrono/c/strftime
// for more information about date/time format
strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct);
return buf;
}
CommandLine::CommandLine() {
this->Title("Console");
message_log_box = new JUI::ScrollingRect(this->ViewportInstance());
message_log_box->Size(JUI::UDim2(0, -input_form_height, 1, 1));
message_log_list = new JUI::VerticalListLayout(message_log_box);
message_log_list->LayoutOrder(JUI::LayoutOrder::V::BOTTOM);
input_box = new JUI::TextInputForm(this->ViewportInstance());
input_box->SetAutoCompleteText(">Type Commands Here");
input_box->Content("");
input_box->Size(JUI::UDim2(0, 20, 1, 0));
input_box->Position(JUI::UDim2(0, -20, 0, 1));
input_box->BGColor(Colors::Grays::DarkSlateGray);
input_box->TextSize(16);
input_box->TextColor(Colors::White);
input_box->SetAutocompleteTextColor(Colors::Gray);
input_box->KeepInputHistory(true);
input_box->OnReturn += [this] (const std::string& msg) {
OnInputBoxSend(msg);
Log(std::format("[{}] >{}", currentDateTime(), msg));
};
}
CommandLine::CommandLine(Widget *parent): CommandLine() {
this->Parent(parent);
}
void CommandLine::SetOpen(bool openVal) {
Window::SetOpen(openVal);
this->input_box->SetFocused(openVal);
}
void CommandLine::Log(const std::string &message, const Color4 &color) {
const Color4 light {60, 60, 60};
const Color4 dark {80, 80, 80};
auto* entry = new JUI::TextRect(message_log_list);
entry->Size({100_percent, UDim(message_height, 0)});
entry->Content(message);
entry->LayoutOrder(index++);
entry->BGColor(index%2 == 0 ? light : dark);
entry->TextColor(color);
entry->TextSize(14);
message_log_box->canvas_height += entry->GetAbsoluteSize().y;
}
bool CommandLine::ObserveKeyInput(Key key, bool pressed) {
if (!IsVisible())
return false;
return Widget::ObserveKeyInput(key, pressed);
}
void CommandLine::OnInputBoxSend(const std::string &message) {
OnInput.Invoke(message);
input_box->Content("");
input_box->SetAutoCompleteText("");
}
}

View File

@@ -0,0 +1,146 @@
#include <JUI/Widgets/ContextMenu.hpp>
namespace JUI {
ContextMenu::ContextMenu(): Rect() {
Name("ContextMenu");
this->BGColor(Colors::White);
//this->Margin(2_px);
this->Size({200, 200, 0, 0});
layout = new VerticalListLayout(this);
MouseExit += [this] (auto _) {
if (!Pinned())
Visible(false);
};
}
ContextMenu::ContextMenu(Widget *parent): ContextMenu() {
this->Parent(parent);
}
void ContextMenu::TextSize(int value) {
TextStyler::TextSize(value);
for (auto* btn : buttons)
btn->TextSize(value);
for (auto* submenu: submenus)
submenu->TextSize(value);
}
void ContextMenu::TextColor(const Color4 &color) {
TextStyler::TextColor(color);
for (auto* btn : buttons)
btn->TextColor(color);
for (auto* submenu: submenus)
submenu->TextColor(color);
}
void ContextMenu::Font(const JGL::Font &value) {
TextStyler::Font(value);
for (auto* btn : buttons)
btn->Font(value);
for (auto* submenu: submenus)
submenu->Font(value);
}
float GuesstimateTextWidth(const std::string& text, int ptSize) {
int length = text.length();
return length * (ptSize/2.f);
}
TextButton * ContextMenu::AddButton(const std::string &name) {
auto* line_item = new TextButton(layout);
line_item->Style(font, 16, Colors::Black);
line_item->Font(font);
line_item->Content(name);
line_item->TextSize(16);
line_item->TextColor(Colors::Black);
line_item->Size({0, 20, 1, 0});
float line_width = GuesstimateTextWidth(line_item->Content(), line_item->TextStyler::TextSize());
if (line_width > estimated_width)
estimated_width = line_width + 10;
// TODO: Temp Hack: Implement Auto-size.
aggregate_height += 20;
this->size.X.Pixels = estimated_width;
this->size.Y.Pixels = aggregate_height;
buttons.push_back(line_item);
return line_item;
}
TextButton * ContextMenu::AddButton(const std::string &name, const std::function<void()> &callback) {
auto* btn = AddButton(name);
// TODO: Is this memory safe at all?
btn->OnReleaseEvent += [this, btn, callback] (auto a, auto b, bool still_focus) mutable {
if (still_focus && this->IsVisible() && btn->IsMouseInside())
callback();
};
return btn;
}
Separator * ContextMenu::AddSeparator(const UDim& size) {
auto* separator = new Separator(layout);
separator->Size({100_percent, size});
// TODO: Temp Hack: Implement Auto-size.
aggregate_height += separator->GetAbsoluteSize().y;
this->size.X.Pixels = estimated_width;
this->size.Y.Pixels = aggregate_height;
return separator;
}
ContextMenu * ContextMenu::AddSubmenu(const std::string &name) {
auto* btn = AddButton(name);
// TODO: For auto-hiding, check if the widget is over a submenu as well.
// TODO: Duplicated code with UtilityBar.cpp
auto* submenu = new JUI::ContextMenu(btn);
submenu->AnchorPoint({0, 0});
submenu->Position({100_percent, 0_percent});
submenu->Visible(false);
btn->OnReleaseEvent += [this, submenu] (auto a, auto b, bool still_there) mutable {
if (still_there && this->IsVisible()) {
this->Pin();
submenu->Visible(true);
}
};
submenu->MouseExit += [this, submenu] (auto _) {
if (!submenu->Pinned())
this->Unpin();
};
submenus.push_back(submenu);
return submenu;
}
void ContextMenu::CloseOnHoverLoss(bool value) { close_on_hover_loss = value;}
bool ContextMenu::CloseOnHoverLoss() const { return close_on_hover_loss; }
void ContextMenu::Update(float delta) {
Rect::Update(delta);
if (parent->IsMouseInside() || IsMouseInside() || IsMouseInsideDescendants())
mouse_outside_tests = 0;
else
mouse_outside_tests++;
if (mouse_outside_tests >= 3)
PresumeMouseFocusLost();
}
}

View File

@@ -0,0 +1,126 @@
#include <JUI/Widgets/FpsGraph.hpp>
namespace JUI {
void FpsGraph::RenderDataPoints() {
J2D::Begin(canvas, nullptr, true);
DataPoint prev = data[0];
for (auto& data_pt : data) {
//J2D::DrawGradientLine(prev.color, data_pt.color, prev.pos, data_pt.pos, 1);
J2D::DrawLine(data_pt.color, data_pt.pos, {data_pt.pos.x, graph_height }, 1);
prev = data_pt;
}
float target_delta = 1.f / target_fps;
float target_line_height = target_delta * y_axis_scale;
float target_fps_rounded = Math::Floor(target_fps);
float target_delta_rounded = Math::Round(target_delta, 1);
std::string target_fps_label_text = std::format("{} FPS / {}ms (Target)", target_fps_rounded, target_delta_rounded);
J2D::DrawString(Colors::Black, target_fps_label_text, 0, graph_height - target_line_height, 10, 1.f);
J2D::DrawLine(Colors::White, {0, graph_height - target_line_height}, {GetAbsoluteSize().x, graph_height - target_line_height}, 1);
J2D::End();
}
FpsGraph::FpsGraph(): JUI::ImageRect() {
BGColor(Colors::Transparent);
BorderColor(Colors::Transparent);
for (int i = 0; i < sample_history; i++) {
Plot({i / 1.f, 0.f}, Colors::Black);
}
canvas = new JGL::RenderTarget(Vector2i(graph_width, graph_height));
RenderDataPoints();
Content(canvas);
}
FpsGraph::FpsGraph(Widget *parent): FpsGraph() {
Parent(parent);
}
void FpsGraph::Plot(const Vector2 &pt, const Color4 &col) {
data.push_back(DataPoint{ pt, col });
}
void FpsGraph::Plot(const DataPoint &data_pt) {
data.push_back(data_pt);
}
void FpsGraph::Update(float delta) {
ImageRect::Update(delta);
data.erase(data.begin());
for (auto& data_pt : data) {
data_pt.pos.x -= 1;
}
Color4 color = Colors::Green;
if (delta > 1.f/HighFPSRange())
color = Colors::Blue;
if (delta > 1.f/TargetFPSRange())
color = Colors::Yellow;
if (delta > 1.f/LowFPSRange())
color = Colors::Red;
if (delta > 1.f/UnplayableFPSRange())
color = Colors::Purples::Magenta;
if (delta > 1.f/5.f)
color = Colors::Black;
Plot({(data.size()-1.f), graph_height - (delta*y_axis_scale)}, color);
RenderDataPoints();
Content(canvas);
}
void FpsGraph::SetupAsPseudoDockedElementAtBottomOfScreen() {
this->Size({100_percent, 50_px});
this->AnchorPoint({1, 1});
this->Position({100_percent, 100_percent});
this->BGColor(Colors::Transparent);
this->BorderColor(Colors::Transparent);
}
void FpsGraph::ShowTargetFPS(bool show) { show_target_fps_bar = show;}
bool FpsGraph::ShowTargetFPS() const { return show_target_fps_bar;}
void FpsGraph::TargetFPS(float fps) {target_fps = fps;}
float FpsGraph::TargetFPS() const { return target_fps; }
float FpsGraph::HighFPSRange() const { return target_fps * high_fps_range_factor;}
float FpsGraph::TargetFPSRange() const { return target_fps * target_fps_range_factor;}
float FpsGraph::SubparFPSRange() const {return target_fps * subpar_fps_range_factor;}
float FpsGraph::LowFPSRange() const { return target_fps * low_fps_range_factor;}
float FpsGraph::UnplayableFPSRange() const { return target_fps * unplayable_fps_range_factor;}
float FpsGraph::SlideshowFPSRange() const { return target_fps * slideshow_fps_range_factor;}
FpsGraphWindow::FpsGraphWindow(): Window() {
Name("FPSGraphWindow");
Title("FPS Graph");
Size({500_px, 70_px});
this->SetResizable(false);
data_graph = new FpsGraph(this->Content());
data_graph->Size({500_px, 50_px});
}
FpsGraphWindow::FpsGraphWindow(Widget *parent): FpsGraphWindow() {
this->Parent(parent);
}
}

View File

@@ -2,9 +2,8 @@
namespace JUI
{
Image::Image() : Widget(), ImageBase()
{
Image::Image() : Widget(), ImageBase() {
Name("Image");
}
Image::Image(Widget* parent) : Image()
@@ -31,7 +30,7 @@ namespace JUI
if (fit_image_to_parent)
{
auto old_scale = scale;
scale = GetAbsoluteSize() / texture->GetDimensions();
scale = GetAbsoluteSize() / Vector2(texture->GetDimensions());
ImageBase::Draw(GetAbsolutePosition(), GetAbsoluteSize());
scale = old_scale;
} else

View File

@@ -0,0 +1,59 @@
#include <JUI/Widgets/ImageButton.hpp>
void JUI::ImageButton::Update(float delta) {
Button::Update(delta);
UpdateImageVisualState();
}
void JUI::ImageButton::Draw() {
Button::Draw();
ImageBase::Draw(GetAbsolutePosition()+GetAbsolutePaddingTopLeft(), GetAbsoluteSize()-GetAbsolutePaddingBottomRight());
}
JUI::ImageButton::ImageButton() : ImageBase(), Button() {
Name("ImageButton");
}
JUI::ImageButton::ImageButton(JUI::Widget *parent) : ImageButton() { Parent(parent); }
void JUI::ImageButton::HoveredImageColor(const Color4 &value) {
hover_img_color = value;
}
void JUI::ImageButton::BaseImageColor(const Color4 &value) {
base_img_color = value;
}
void JUI::ImageButton::PressedImageColor(const Color4 &value) {
pressed_img_color = value;
}
void JUI::ImageButton::DisabledImageColor(const Color4 &value) {
disabled_img_color = value;
}
void JUI::ImageButton::UpdateImageVisualState() {
if (Disabled())
ImageBase::Color(DisabledImageColor());
else if (IsClicked())
ImageBase::Color(PressedImageColor());
else if (IsHovered())
ImageBase::Color(HoveredImageColor());
else
ImageBase::Color(BaseImageColor());
}
Color4 JUI::ImageButton::HoveredImageColor() const {return hover_img_color;}
Color4 JUI::ImageButton::BaseImageColor() const { return base_img_color; }
Color4 JUI::ImageButton::PressedImageColor() const { return pressed_img_color; }
Color4 JUI::ImageButton::DisabledImageColor() const { return disabled_img_color; }
void JUI::ImageButton::ImageColors(const Color4 &hover, const Color4 &base, const Color4 &press, const Color4 &disabled) {
HoveredImageColor(hover);
BaseImageColor(base);
PressedImageColor(press);
DisabledImageColor(disabled);
}

View File

@@ -1,2 +1,18 @@
#include <JUI/Widgets/ImageRect.hpp>
JUI::ImageRect::ImageRect() : Rect(), ImageBase() {
Name("ImageRect");
}
JUI::ImageRect::ImageRect(JUI::Widget *parent) : ImageRect() {
Parent(parent);
}
void JUI::ImageRect::Update(float delta) {
Rect::Update(delta);
}
void JUI::ImageRect::Draw() {
Rect::Draw();
ImageBase::Draw(GetAbsolutePosition()+GetAbsolutePaddingTopLeft(), GetAbsoluteSize()-GetAbsolutePaddingBottomRight());
}

View File

@@ -0,0 +1,26 @@
#include <JUI/Widgets/LabeledSlider.hpp>
JUI::LabeledSlider::LabeledSlider(): Slider(), TextBase() {
}
JUI::LabeledSlider::LabeledSlider(Widget *parent): LabeledSlider() {
this->Parent(parent);
}
void JUI::LabeledSlider::Draw() {
Slider::Draw();
auto abs_pos = this->GetAbsolutePosition();
auto abs_size = this->GetAbsoluteSize();
auto pos_pad = GetAbsolutePaddingTopLeft();
auto size_pad = GetAbsolutePaddingBottomRight();
TextBase::Draw(abs_pos + pos_pad, abs_size - size_pad);
}
void JUI::LabeledSlider::Update(float elapsed) {
Slider::Update(elapsed);
TextBase::Update(elapsed);
}

View File

@@ -2,17 +2,28 @@
namespace JUI
{
ListLayout::ListLayout() : LayoutContainer() {}
ListLayout::ListLayout(Widget* parent) : ListLayout() {}
ListLayout::ListLayout() : LayoutContainer() {
Name("ListLayout");
}
ListLayout::ListLayout(Widget* parent) : ListLayout() {
Parent(parent);
}
VerticalListLayout::VerticalListLayout() : ListLayout() {}
VerticalListLayout::VerticalListLayout(Widget* parent) : VerticalListLayout()
{
VerticalListLayout::VerticalListLayout(Widget* parent) : VerticalListLayout() {
this->Parent(parent);
}
struct {
bool operator()(Widget* a, Widget* b) const { return a->LayoutOrder() > b->LayoutOrder();}
} layoutOrderSort;
void VerticalListLayout::ApplyLayout() {
content_height = 0;
std::sort(children.begin(), children.end(), layoutOrderSort);
if (layout == LayoutOrder::V::TOP)
{
int consumed_height = 0;
@@ -21,10 +32,18 @@ namespace JUI
// TODO: Implement widget.LayoutOrder property
// TODO: Sort children by LayoutOrder
consumed_height += pad_top.Pixels;
content_height += pad_top.Pixels;
child_widget->Position({0, consumed_height, 0, 0});
consumed_height += child_widget->GetAbsoluteSize().y;
content_height += child_widget->GetAbsoluteSize().y;
consumed_height += pad_bottom.Pixels;
content_height += pad_bottom.Pixels;
}
}
if (layout == LayoutOrder::V::BOTTOM)
@@ -34,10 +53,18 @@ namespace JUI
{
// TODO: Implement widget.LayoutOrder property
// TODO: Sort children by LayoutOrder
consumed_height -= pad_top.Pixels;
content_height += pad_top.Pixels;
consumed_height -= child_widget->GetAbsoluteSize().y;
content_height += child_widget->GetAbsoluteSize().y;
child_widget->Position({0, consumed_height, 0, 0});
consumed_height -= pad_bottom.Pixels;
content_height += pad_bottom.Pixels;
}
}
}
@@ -48,6 +75,10 @@ namespace JUI
LayoutOrder::V VerticalListLayout::LayoutOrder() const { return layout;}
float VerticalListLayout::CurrentContentHeight() const {
return content_height;
}
HorizontalListLayout::HorizontalListLayout() : ListLayout() {}
@@ -58,15 +89,25 @@ namespace JUI
void HorizontalListLayout::ApplyLayout() {
// TODO: Implement widget.LayoutOrder and sort by that number.
std::sort(children.begin(), children.end(), layoutOrderSort);
if (layout == LayoutOrder::H::LEFT)
{
int consumed_width = 0;
for (auto &child_widget : children)
{
consumed_width += pad_left.Pixels;
content_width += pad_left.Pixels;
child_widget->Position({consumed_width, 0, 0, 0});
consumed_width += child_widget->GetAbsoluteSize().x;
content_width += child_widget->GetAbsoluteSize().x;
consumed_width += pad_right.Pixels;
content_width += pad_right.Pixels;
}
}
@@ -76,9 +117,15 @@ namespace JUI
for (auto &child_widget : children)
{
consumed_width -= pad_left.Pixels;
content_width += pad_left.Pixels;
consumed_width -= child_widget->GetAbsoluteSize().x;
content_width += child_widget->GetAbsoluteSize().x;
child_widget->Position({consumed_width, 0, 0, 0});
consumed_width -= pad_right.Pixels;
content_width += pad_right.Pixels;
}
}
}
@@ -90,4 +137,6 @@ namespace JUI
LayoutOrder::H HorizontalListLayout::LayoutOrder() const {
return layout;
}
float HorizontalListLayout::CurrentContentWidth() const { return content_width;}
}

View File

@@ -0,0 +1,108 @@
#include <JUI/Widgets/NineSlice.hpp>
#include <JGL/JGL.h>
namespace JUI {
AABB2D JUI::NineSliceRect::TopLeftQuad() const { return top_left_quad; }
AABB2D NineSliceRect::TopRightQuad() const { return top_right_quad; }
AABB2D NineSliceRect::BottomLeftQuad() const { return bottom_left_quad; }
AABB2D NineSliceRect::BottomRightQuad() const { return bottom_right_quad; }
AABB2D NineSliceRect::TopQuad() const { return top_quad; }
AABB2D NineSliceRect::LeftQuad() const { return left_quad; }
AABB2D NineSliceRect::RightQuad() const { return right_quad; }
AABB2D NineSliceRect::BottomQuad() const { return bottom_quad; }
AABB2D NineSliceRect::CenterQuad() const { return center_quad; }
void NineSliceRect::TopLeftQuad(const AABB2D &quad) { top_left_quad = quad; }
void NineSliceRect::TopRightQuad(const AABB2D &quad) { top_right_quad = quad; }
void NineSliceRect::BottomLeftQuad(const AABB2D &quad) { bottom_left_quad = quad; }
void NineSliceRect::BottomRightQuad(const AABB2D &quad) { bottom_right_quad = quad; }
void NineSliceRect::TopQuad(const AABB2D &quad) { top_quad = quad;}
void NineSliceRect::RightQuad(const AABB2D &quad) { right_quad = quad;}
void NineSliceRect::BottomQuad(const AABB2D &quad) { bottom_quad = quad;}
void NineSliceRect::LeftQuad(const AABB2D &quad) { left_quad = quad;}
void NineSliceRect::CenterQuad(const AABB2D &quad) { center_quad = quad;}
void NineSliceRect::Draw() {
Rect::Draw();
Vector2 abs_pos = GetAbsolutePosition();
Vector2 abs_size = GetAbsoluteSize();
// Draw Top-Left Quad.
Vector2 tl_computed_pos = abs_pos;
auto tl_quad = TopLeftQuad();
JGL::J2D::DrawPartialRenderTarget(texture, tl_computed_pos, tl_quad.minPoint, tl_quad.maxPoint);
// Draw Top-Right Quad.
auto tr_quad = TopRightQuad();
Vector2 tr_computed_pos = abs_pos + Vector2(abs_size.x - tr_quad.maxPoint.x, 0);
JGL::J2D::DrawPartialRenderTarget(texture, tr_computed_pos, tr_quad.minPoint, tr_quad.maxPoint);
// Draw Bottom Left Quad
auto bl_quad = BottomLeftQuad();
Vector2 bl_computed_pos = abs_pos + Vector2(0, abs_size.y - bl_quad.maxPoint.y);
JGL::J2D::DrawPartialRenderTarget(texture, bl_computed_pos, bl_quad.minPoint, bl_quad.maxPoint);
// Draw Bottom Right Quad
auto br_quad = BottomRightQuad();
Vector2 br_computed_pos = abs_pos + abs_size - br_quad.maxPoint;
JGL::J2D::DrawPartialRenderTarget(texture, br_computed_pos, br_quad.minPoint, br_quad.maxPoint);
// Draw Top-Quad.
Vector2 t_computed_pos = abs_pos + Vector2(tl_quad.maxPoint.x, 0);
auto t_quad = TopQuad();
float abs_width_minus_corners = abs_size.x - (tl_quad.maxPoint.x + tr_quad.maxPoint.x);
float t_scaling = abs_width_minus_corners / t_quad.maxPoint.x;
JGL::J2D::DrawPartialRenderTarget(texture, t_computed_pos, t_quad.minPoint, t_quad.maxPoint, 0, {0,0}, {t_scaling, 1});
// Draw Bottom Quad
auto b_quad = BottomQuad();
Vector2 b_computed_pos = abs_pos + Vector2(tl_quad.maxPoint.x, abs_size.y - b_quad.maxPoint.y);
float b_scaling = abs_width_minus_corners / b_quad.maxPoint.x;
JGL::J2D::DrawPartialRenderTarget(texture, b_computed_pos, b_quad.minPoint, b_quad.maxPoint, 0, {0,0}, {b_scaling, 1});
// Draw Left Quad
Vector2 l_computed_pos = abs_pos + Vector2(0, tl_quad.maxPoint.y);
auto l_quad = LeftQuad();
float abs_height_minus_corners = abs_size.y - (tl_quad.maxPoint.y + tr_quad.maxPoint.y);
float l_scaling = abs_height_minus_corners / l_quad.maxPoint.y;
JGL::J2D::DrawPartialRenderTarget(texture, l_computed_pos, l_quad.minPoint, l_quad.maxPoint, 0, {0, 0}, {1, l_scaling});
// Draw Right Quad
auto r_quad = RightQuad();
Vector2 r_computed_pos = abs_pos + Vector2(abs_size.x - tr_quad.maxPoint.x, tr_quad.maxPoint.y);
float r_scaling = abs_height_minus_corners / r_quad.maxPoint.y;
JGL::J2D::DrawPartialRenderTarget(texture, r_computed_pos, r_quad.minPoint, r_quad.maxPoint, 0, {0,0}, {1, r_scaling});
// Draw Center Quad
auto c_quad = CenterQuad();
Vector2 c_computed_pos = abs_pos + tl_quad.maxPoint;
Vector2 c_scaling = Vector2(abs_width_minus_corners, abs_height_minus_corners) / c_quad.maxPoint;
JGL::J2D::DrawPartialRenderTarget(texture, c_computed_pos, c_quad.minPoint, c_quad.maxPoint, 0, {0,0}, c_scaling);
}
NineSliceRect::NineSliceRect() : Rect(), ImageBase() {
Name("NineSliceRect");
}
NineSliceRect::NineSliceRect(Widget *parent) : Rect(parent), ImageBase() {}
NineSliceRect::NineSliceRect(Widget *parent, JGL::Texture *texture) : Rect(parent), ImageBase(texture) {}
}

View File

@@ -1,41 +1,20 @@
#include <JUI/Mixins/Toggleable.hpp>
#include <JUI/Widgets/RadioButton.hpp>
namespace JUI
{
RadioButton::RadioButton() : Button(), Toggleable() {}
namespace JUI {
RadioButton::RadioButton() : Button(), Toggleable() {
Name("RadioButton");
}
RadioButton::RadioButton(Widget* parent) : RadioButton() {
this->Parent(parent);
}
/*
void RadioButton::OnToggleOn() {
OnToggleOnEvent.Invoke();
}
void RadioButton::OnToggleOff() {
OnToggleOffEvent.Invoke();
}
*/
void RadioButton::OnClick(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &btn) {
Button::OnClick(mouse_pos, btn);
Toggleable::Update();
/*
toggle = !toggle;
if (toggle) {
OnToggleOn();
} else {
OnToggleOff();
}
*/
}
void RadioButton::Update(float delta)
{
void RadioButton::Update(float delta) {
Button::Update(delta);
}
}

View File

@@ -3,14 +3,15 @@
#include <jlog/Logger.hpp>
namespace JUI {
Rect::Rect(): Widget(), RectBase() {}
Rect::Rect(): Widget(), RectBase() {
Name("Rect");
}
Rect::Rect(Widget *parent): Rect() {
this->Parent(parent);
}
void Rect::PreDraw()
{
Vector2 abs_pos = GetAbsolutePosition();
@@ -42,25 +43,17 @@ namespace JUI {
}
}
void Rect::InnerDraw()
{
J2D::Begin();
void Rect::InnerDraw() {
RectBase::Draw(GetAbsolutePosition(), GetAbsoluteSize());
J2D::End();
// Draw Child Elements with scissor clipping still active
//Widget::DrawChildWidgets();
}
void Rect::Update(float delta) {
if (IsMouseInside() && !mouse_inside_debounce)
{
if (IsMouseInside() && !mouse_inside_debounce) {
MouseEnter.Invoke(last_known_mouse_pos);
mouse_inside_debounce = true;
}
if (!IsMouseInside() && mouse_inside_debounce)
{
if (!IsMouseInside() && mouse_inside_debounce) {
MouseExit.Invoke(last_known_mouse_pos);
mouse_inside_debounce = false;
}
@@ -87,7 +80,6 @@ namespace JUI {
void Rect::Draw() {
Widget::Draw();
}
}

View File

@@ -1,18 +1,36 @@
#include "JUI/Widgets/Scene.hpp"
namespace JUI {
Scene::Scene(): Widget() {}
Scene::Scene(): Widget() {
Name("Scene");
}
Vector2 Scene::GetAbsolutePosition() const { return {0,0};}
Vector2 Scene::GetAbsoluteSize() const { return viewport_size;}
Vector2 Scene::GetAbsoluteSize() const { return viewport_size / ui_scale;}
void Scene::Draw() {
Widget::Draw();
J2D::Begin();
glScalef(ui_scale.x, ui_scale.y, 0.f);
Widget::Draw();
J2D::End();
}
void Scene::Update(float delta) {
Widget::Update(delta);
}
void Scene::GlobalUIScale(const Vector2 &value) { ui_scale = value;}
Vector2 Scene::GlobalUIScale() const { return ui_scale; }
bool Scene::ObserveMouseMovement(const Vector2 &latest_known_pos) {
Vector2 new_pos = latest_known_pos / ui_scale;
return Widget::ObserveMouseMovement(new_pos);
}
}

Some files were not shown because too many files have changed in this diff Show More