Compare commits
74 Commits
Prerelease
...
master
Author | SHA1 | Date | |
---|---|---|---|
c8946673c3 | |||
d0a246bffe | |||
e084e1120f | |||
238a7934b3 | |||
558f17fc94 | |||
4acd91b078 | |||
944bd66b43 | |||
d935608e05 | |||
3b973c2c45 | |||
ce0c69190a | |||
dca15511ea | |||
beb5c97c13 | |||
505c2c70e6 | |||
9b07a8ea01 | |||
393ad1b2b3 | |||
67416540d4 | |||
d9d92c2a28 | |||
086a3f226b | |||
a2088b086b | |||
86fb0cb2e6 | |||
3a0743e787 | |||
f46bf097ac | |||
b80fa9cd94 | |||
f229698971 | |||
96239db592 | |||
9d901c6300 | |||
d5d6703ee9 | |||
72e21451a6 | |||
8af6030d1a | |||
0e62fcd5f6 | |||
938be6f7ca | |||
1c523dafa2 | |||
80682e5fee | |||
fa5b9e23cf | |||
3a0901693e | |||
4415e3c6b4 | |||
1bd581a5d8 | |||
31d5074ed0 | |||
ab11857cfa | |||
37d14cc0e0 | |||
134fa46f5a | |||
5f53938f67 | |||
48b56213d8 | |||
7ac0436a1f | |||
c4ada58e0c | |||
21fee89e77 | |||
0ff787fd95 | |||
75f30b062a | |||
3033edb903 | |||
814a7a8a3c | |||
c538937c07 | |||
c183cae173 | |||
bd8e9222de | |||
672429f736 | |||
abe4371f00 | |||
360171b46f | |||
d8c48a338d | |||
faf312206e | |||
1080a886cb | |||
b01b975ce9 | |||
5ab0d89793 | |||
d807faea58 | |||
eab4165417 | |||
cf8c86c7c7 | |||
b0c3203f70 | |||
2fed2ea760 | |||
06f27b2436 | |||
543b1f81a4 | |||
6f7388a27d | |||
c3e372ac29 | |||
fee9f2083c | |||
fdaf68b01e | |||
62cfd1bd58 | |||
b7ab9a8780 |
@@ -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,19 +26,19 @@ 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 jlog
|
||||
URL https://git.redacted.cc/josh/jlog/archive/Prerelease-17.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME mcolor
|
||||
URL https://git.redacted.cc/maxine/mcolor/archive/Prerelease-6.2.zip
|
||||
URL https://git.redacted.cc/maxine/mcolor/archive/Release-1.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
@@ -46,6 +46,11 @@ CPMAddPackage(
|
||||
URL https://git.redacted.cc/josh/Event/archive/Release-12.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME jlog
|
||||
URL https://git.redacted.cc/josh/jlog/archive/Prerelease-19.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME J3ML
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/3.4.5.zip
|
||||
@@ -53,12 +58,12 @@ CPMAddPackage(
|
||||
|
||||
CPMAddPackage(
|
||||
NAME ReWindow
|
||||
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-32.zip
|
||||
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-34.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME JGL
|
||||
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-52.zip
|
||||
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-58.zip
|
||||
)
|
||||
|
||||
target_include_directories(JUI PUBLIC ${Event_SOURCE_DIR}/include)
|
||||
|
23
README.md
23
README.md
@@ -15,24 +15,7 @@ It is expressly built with our Redacted3D engine in mind, but steps have been ta
|
||||
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
|
||||
* Comprehensive list of common UI widgets:
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* Vertical and Horizontal ListLayout, GridLayout
|
||||
* Separator
|
||||
* ScrollRect
|
||||
* 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.
|
||||
@@ -58,6 +41,10 @@ Currently, the package is also integrated with 
|
||||
|
||||
[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!
|
||||
|
0
docs/animation.md
Normal file
0
docs/animation.md
Normal file
0
docs/layout.md
Normal file
0
docs/layout.md
Normal file
29
docs/overview.md
Normal file
29
docs/overview.md
Normal 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
0
docs/styling.md
Normal file
300
docs/widgets.md
Normal file
300
docs/widgets.md
Normal 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>
|
12
include/JUI/Base/CanvasBase.hpp
Normal file
12
include/JUI/Base/CanvasBase.hpp
Normal 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 {
|
||||
|
||||
|
||||
};
|
||||
}
|
@@ -14,6 +14,7 @@
|
||||
#include <Color4.hpp>
|
||||
#include <J3ML/LinearAlgebra/Vector2.hpp>
|
||||
#include <JGL/types/Texture.h>
|
||||
#include <JGL/types/RenderTarget.h>
|
||||
|
||||
using J3ML::LinearAlgebra::Vector2;
|
||||
|
||||
@@ -27,11 +28,15 @@ namespace JUI {
|
||||
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.
|
||||
@@ -40,7 +45,8 @@ 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.
|
||||
@@ -52,7 +58,7 @@ namespace JUI {
|
||||
/// 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);
|
||||
|
@@ -18,6 +18,8 @@
|
||||
#include "JGL/types/RenderTarget.h"
|
||||
#include <JUI/DefaultStyle.hpp>
|
||||
|
||||
#include "JGL/JGL.h"
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
enum class BorderMode
|
||||
@@ -27,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:
|
||||
@@ -40,6 +46,7 @@ namespace JUI
|
||||
virtual void CornerRounding(float radius);
|
||||
|
||||
[[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);
|
||||
@@ -67,9 +74,29 @@ 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;
|
||||
enum CornerRoundingMode corner_mode = CornerRoundingMode::Rounded;
|
||||
bool mouse_press_debounce{};
|
||||
bool mouse_inside_debounce{};
|
||||
float border_width = Style::BorderLineWidth;
|
||||
|
@@ -16,6 +16,8 @@
|
||||
#include <JGL/JGL.h>
|
||||
#include <JGL/types/Font.h>
|
||||
|
||||
#include "../Style/TextStyler.hpp"
|
||||
|
||||
using J3ML::LinearAlgebra::Vector2;
|
||||
namespace JUI {
|
||||
class TextBase;
|
||||
@@ -40,22 +42,22 @@ namespace JUI::TextAlign {
|
||||
}
|
||||
|
||||
/// TextBase class, implements core mechanics of drawing text in JUI, and is used by Text Widget class.
|
||||
class JUI::TextBase {
|
||||
private:
|
||||
bool state_redraw = true;
|
||||
protected:
|
||||
RenderTarget* text_canvas;
|
||||
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;
|
||||
protected:
|
||||
class JUI::TextBase : public TextStyler {
|
||||
public:
|
||||
#pragma region Constructors
|
||||
~TextBase() { delete text_canvas; };
|
||||
|
||||
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
|
||||
|
||||
#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);
|
||||
@@ -63,40 +65,60 @@ protected:
|
||||
/// 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:
|
||||
|
||||
public:
|
||||
[[nodiscard]] std::string GetContent() const;
|
||||
[[nodiscard]] Color4 GetTextColor() const;
|
||||
[[nodiscard]] Color4 GetOutlineColor() const;
|
||||
[[nodiscard]] TextAlign::H GetHorizontalTextAlign() const;
|
||||
[[nodiscard]] TextAlign::V GetVerticalTextAlign() const;
|
||||
TextOverflowMode GetOverflowMode() const;
|
||||
[[nodiscard]] Vector2 GetTextBounds();
|
||||
[[nodiscard]] Vector2 GetTextPosition() const;
|
||||
[[nodiscard]] JGL::Font GetFont() const;
|
||||
[[nodiscard]] u32 GetTextSize() const;
|
||||
[[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 Update(float delta);
|
||||
void SetWordWrap(bool wrap);
|
||||
void SetOverflowMode(const TextOverflowMode& mode);
|
||||
void SetTextSize(u32 size);
|
||||
void SetFont(const JGL::Font& font);
|
||||
void SetContent(const std::string& content);
|
||||
void SetTextColor(const Color4& color);
|
||||
void SetOutlineColor(const Color4& color);
|
||||
|
||||
void WordWrap(bool wrap);
|
||||
void OverflowMode(const TextOverflowMode& mode);
|
||||
|
||||
void Content(const std::string& content);
|
||||
|
||||
/// @note This member is deprecated in favor of TextSize().
|
||||
void TextSize(int size) override;
|
||||
|
||||
/// @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;
|
||||
|
||||
void OutlineColor(const Color4& color);
|
||||
|
||||
/// 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);
|
||||
void HorizontalTextAlign(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 VerticalTextAlign(const TextAlign::V& align);
|
||||
|
||||
/// 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);
|
||||
void Align(const TextAlign::H& h_align, const TextAlign::V& v_align);
|
||||
|
||||
/// Aligns the text of this widget to the left-hand-side of the parent's bounding box.
|
||||
/// @see SetHorizontalTextAlign, TextAlign::H
|
||||
@@ -126,7 +148,5 @@ public:
|
||||
|
||||
/// Aligns the text of this widget to the vertical center of the parent's bounding box.
|
||||
void AlignCenterVertically();
|
||||
public:
|
||||
~TextBase() { delete text_canvas; };
|
||||
TextBase() : set_font(JGL::Fonts::Jupiteroid), state_redraw(true), text_canvas(new RenderTarget({1, 1}, {0, 0, 0, 0})) {};
|
||||
|
||||
};
|
@@ -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>
|
||||
@@ -31,17 +41,43 @@ 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 {
|
||||
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.
|
||||
@@ -81,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();
|
||||
|
||||
@@ -90,6 +129,8 @@ 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;
|
||||
@@ -108,6 +149,8 @@ namespace JUI {
|
||||
/// In a well-formed JUI menu, this **should** always be a Scene.
|
||||
Widget* GetFamilyTreeRoot() const;
|
||||
|
||||
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Layout
|
||||
@@ -123,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().
|
||||
@@ -282,9 +325,17 @@ namespace JUI {
|
||||
|
||||
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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;}
|
||||
|
||||
@@ -325,6 +376,7 @@ namespace JUI {
|
||||
/// 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.
|
||||
@@ -357,8 +409,8 @@ namespace JUI {
|
||||
UDim2 position = {0_px, 0_px};
|
||||
UDim2 size = {50_px, 50_px};
|
||||
Widget* parent = nullptr;
|
||||
std::vector<Widget*> children;
|
||||
std::vector<Tween*> tweens;
|
||||
std::vector<Widget*> children{};
|
||||
std::vector<Tween*> tweens{};
|
||||
float rotation = 0;
|
||||
std::string name;
|
||||
bool selected = false;
|
||||
@@ -375,9 +427,12 @@ namespace JUI {
|
||||
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;
|
||||
@@ -388,5 +443,8 @@ namespace JUI {
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
5
include/JUI/Experimental.hpp
Normal file
5
include/JUI/Experimental.hpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace JUI::Experimental {
|
||||
|
||||
}
|
@@ -4,7 +4,25 @@
|
||||
#include <Event.h>
|
||||
|
||||
namespace JUI {
|
||||
class JUILogger : public jlog::GenericLogger {
|
||||
|
||||
|
||||
/// 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;
|
||||
|
||||
@@ -20,12 +38,4 @@ class JUILogger : public jlog::GenericLogger {
|
||||
};
|
||||
|
||||
extern JUILogger UILogs;
|
||||
|
||||
/// 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
|
||||
};
|
||||
}
|
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
8
include/JUI/Mixins/DragAndDropReceiver.hpp
Normal file
8
include/JUI/Mixins/DragAndDropReceiver.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// Created by josh on 4/11/25.
|
||||
//
|
||||
|
||||
#ifndef DRAGANDDROPRECEIVER_HPP
|
||||
#define DRAGANDDROPRECEIVER_HPP
|
||||
|
||||
#endif //DRAGANDDROPRECEIVER_HPP
|
8
include/JUI/Mixins/DragAndDropSource.hpp
Normal file
8
include/JUI/Mixins/DragAndDropSource.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// Created by josh on 4/11/25.
|
||||
//
|
||||
|
||||
#ifndef DRAGANDDROPSOURCE_HPP
|
||||
#define DRAGANDDROPSOURCE_HPP
|
||||
|
||||
#endif //DRAGANDDROPSOURCE_HPP
|
62
include/JUI/Mixins/Focusable.hpp
Normal file
62
include/JUI/Mixins/Focusable.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
|
@@ -18,19 +18,39 @@
|
||||
|
||||
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);
|
||||
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;
|
||||
|
5
include/JUI/Mixins/NestableMenu.hpp
Normal file
5
include/JUI/Mixins/NestableMenu.hpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace JUI {
|
||||
|
||||
}
|
57
include/JUI/Mixins/Pinnable.hpp
Normal file
57
include/JUI/Mixins/Pinnable.hpp
Normal 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;
|
||||
};
|
||||
}
|
14
include/JUI/Style/ButtonStyler.hpp
Normal file
14
include/JUI/Style/ButtonStyler.hpp
Normal 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 {
|
||||
|
||||
};
|
||||
}
|
15
include/JUI/Style/CheckboxStyler.hpp
Normal file
15
include/JUI/Style/CheckboxStyler.hpp
Normal 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:
|
||||
};
|
||||
}
|
45
include/JUI/Style/RectStyler.hpp
Normal file
45
include/JUI/Style/RectStyler.hpp
Normal 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:
|
||||
};
|
||||
}
|
45
include/JUI/Style/TextStyler.hpp
Normal file
45
include/JUI/Style/TextStyler.hpp
Normal 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;
|
||||
};
|
||||
}
|
@@ -26,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;
|
||||
|
||||
|
@@ -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;
|
||||
|
94
include/JUI/Widgets/Anchor.hpp
Normal file
94
include/JUI/Widgets/Anchor.hpp
Normal 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:
|
||||
|
||||
};
|
||||
}
|
@@ -32,6 +32,7 @@ namespace JUI {
|
||||
protected:
|
||||
bool checked = false;
|
||||
Color4 check_color = Style::Checkbox::CheckmarkColor;
|
||||
Color4 uncheck_color = Style::BackgroundColor;
|
||||
};
|
||||
|
||||
class Checkbox : public Button, public CheckboxBase {
|
||||
|
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
/// A composite class which combines a Checkbox and TextLabel into one element.
|
||||
class CheckboxLabel : public Rect
|
||||
{
|
||||
|
||||
};
|
||||
}
|
@@ -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:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -24,10 +24,6 @@ namespace JUI {
|
||||
|
||||
/// This event is invoked when the user has sent a message to the command line.
|
||||
Event<std::string> OnInput;
|
||||
/// This event is invoked when the CommandLine window is opened.
|
||||
Event<> OnOpen;
|
||||
/// This event is invoked when the CommandLine window is closed.
|
||||
Event<> OnClose;
|
||||
|
||||
/// The default constructor initializes the layout and events of child elements.
|
||||
/// This constructor is a delegate called by the explicit constructor with parent parameter.
|
||||
@@ -42,18 +38,8 @@ namespace JUI {
|
||||
/// @param color
|
||||
void Log(const std::string& message, const Color4& color = Colors::White);
|
||||
|
||||
/// @return True if the window widget is currently open and visible. Input will be ignored if not.
|
||||
[[nodiscard]] bool Opened() const;
|
||||
|
||||
/// @return True if the window widget is currently closed and invisible. Input will be ignored if so.
|
||||
[[nodiscard]] bool Closed() const;
|
||||
|
||||
/// Opens the window widget.
|
||||
void Open(bool openVal = true);
|
||||
/// Closes the window widget.
|
||||
void Close(bool openVal = false);
|
||||
/// 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);
|
||||
void SetOpen(bool openVal) override;
|
||||
|
||||
/// Passes input events down the widget hierarchy.
|
||||
/// @return True if this widget will "consume" the input.
|
||||
@@ -70,7 +56,6 @@ namespace JUI {
|
||||
int index = 0;
|
||||
int input_form_height = 20;
|
||||
int message_height = 16;
|
||||
bool open = false;
|
||||
std::vector<std::string> msg_history;
|
||||
private:
|
||||
};
|
||||
|
96
include/JUI/Widgets/ContextMenu.hpp
Normal file
96
include/JUI/Widgets/ContextMenu.hpp
Normal 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:
|
||||
|
||||
};
|
||||
}
|
150
include/JUI/Widgets/FileDialog.hpp
Normal file
150
include/JUI/Widgets/FileDialog.hpp
Normal 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 { };
|
||||
|
||||
}
|
119
include/JUI/Widgets/FpsGraph.hpp
Normal file
119
include/JUI/Widgets/FpsGraph.hpp
Normal 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:
|
||||
};
|
||||
}
|
36
include/JUI/Widgets/Graph.hpp
Normal file
36
include/JUI/Widgets/Graph.hpp
Normal 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:
|
||||
};
|
||||
|
||||
}
|
27
include/JUI/Widgets/LabeledCheckbox.hpp
Normal file
27
include/JUI/Widgets/LabeledCheckbox.hpp
Normal 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();
|
||||
};
|
||||
}
|
19
include/JUI/Widgets/LabeledSlider.hpp
Normal file
19
include/JUI/Widgets/LabeledSlider.hpp
Normal 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:
|
||||
};
|
||||
}
|
113
include/JUI/Widgets/Link.hpp
Normal file
113
include/JUI/Widgets/Link.hpp
Normal 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:
|
||||
};
|
||||
}
|
@@ -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);
|
||||
};
|
||||
|
||||
}
|
40
include/JUI/Widgets/RadioButtonSet.hpp
Normal file
40
include/JUI/Widgets/RadioButtonSet.hpp
Normal 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;
|
||||
|
||||
};
|
||||
|
||||
}
|
@@ -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.
|
||||
|
@@ -1,24 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Base/Widget.hpp>
|
||||
#include <JUI/JUI.hpp>
|
||||
|
||||
namespace JUI {
|
||||
|
||||
enum class Orientation { HORIZONTAL, VERTICAL };
|
||||
|
||||
/// 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:
|
||||
Separator() : Widget()
|
||||
{
|
||||
Name("Separator");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// 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();
|
||||
}
|
||||
|
||||
explicit Separator(Widget* parent)
|
||||
{
|
||||
|
||||
/// @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:
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -18,56 +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;
|
||||
|
||||
/// 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;
|
||||
/// @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);
|
||||
|
||||
/// @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);
|
||||
void ScrubberWidth(float width);
|
||||
/// @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;
|
||||
float percentage = 0.f;
|
||||
bool dragging = false;
|
||||
float scrubber_width = 20;
|
||||
UDim2 scrubber_size {20_px, 100_percent};
|
||||
Color4 scrubber_color = Colors::White;
|
||||
float scrubber_rounding = 0.f;
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
class VerticalSlider {
|
||||
public:
|
||||
protected:
|
||||
private:
|
||||
|
||||
};
|
||||
}
|
||||
|
61
include/JUI/Widgets/TabView.hpp
Normal file
61
include/JUI/Widgets/TabView.hpp
Normal 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:
|
||||
};
|
||||
}
|
@@ -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:
|
||||
|
@@ -27,18 +27,32 @@ namespace JUI {
|
||||
|
||||
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
|
||||
|
||||
void MoveCursorLeft();
|
||||
#pragma endregion
|
||||
#pragma region Setters
|
||||
|
||||
void MoveCursorRight();
|
||||
#pragma endregion
|
||||
|
||||
/// Returns the maximum position of the cursor, which is determined by the input buffer's length.
|
||||
[[nodiscard]] unsigned int CursorMaxPosition() const;
|
||||
@@ -64,6 +78,11 @@ namespace JUI {
|
||||
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;
|
||||
@@ -103,22 +122,50 @@ namespace JUI {
|
||||
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;
|
||||
bool selection_enabled;
|
||||
bool selection_active;
|
||||
int selection_start_index;
|
||||
int selection_end_index;
|
||||
unsigned int cursor_position = 0;
|
||||
std::string input_buffer;
|
||||
|
||||
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:
|
||||
};
|
||||
}
|
10
include/JUI/Widgets/ToggleButton.hpp
Normal file
10
include/JUI/Widgets/ToggleButton.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include "Button.hpp"
|
||||
|
||||
|
||||
namespace JUI {
|
||||
/// The
|
||||
class ToggleButton : public Button {
|
||||
|
||||
};
|
||||
}
|
@@ -12,32 +12,24 @@ namespace JUI
|
||||
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
|
||||
{
|
||||
void Update(float delta) override;
|
||||
|
||||
if (parent && parent->IsMouseInside() || IsMouseInside())
|
||||
{
|
||||
auto coords = InputService::GetMousePosition();
|
||||
Position(UDim2::FromPixels(coords.x, coords.y));
|
||||
Visible(true);
|
||||
} else
|
||||
Visible(false);
|
||||
|
||||
|
||||
TextRect::Update(delta);
|
||||
}
|
||||
~Tooltip() override {};
|
||||
|
||||
Widget* attachment = nullptr;
|
||||
|
||||
float PopupDelay() const { return 0;}
|
||||
|
||||
protected:
|
||||
float popup_delay = 0.f;
|
||||
};
|
||||
}
|
@@ -1,107 +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() {
|
||||
Name("ContextMenu");
|
||||
this->BGColor(Colors::White);
|
||||
this->Margin(2_px);
|
||||
this->Size({200, 200, 0, 0});
|
||||
layout = new VerticalListLayout(this);
|
||||
|
||||
MouseExit += [this] (Vector2 _)
|
||||
{
|
||||
this->Visible(false);
|
||||
this->Parent(nullptr);
|
||||
// TODO: Collect
|
||||
};
|
||||
}
|
||||
|
||||
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->BorderWidth(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->BorderWidth(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:
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -34,6 +34,9 @@ namespace JUI {
|
||||
/// 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 {
|
||||
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.
|
||||
@@ -55,24 +58,41 @@ namespace JUI {
|
||||
// TODO: Decide if this will auto-scale with the titlebar's text height, or the other way around.
|
||||
[[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() const;
|
||||
void MoveUp() {
|
||||
zindex++;
|
||||
}
|
||||
/// Moves this window down in the window-widget stack.
|
||||
void MoveDown() const;
|
||||
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;
|
||||
@@ -106,6 +126,8 @@ namespace JUI {
|
||||
Rect* TopbarInstance();
|
||||
/// Returns a pointer to the Rect Widget that is used to layout the contents of the window.
|
||||
Rect* ViewportInstance();
|
||||
Rect* Content();
|
||||
|
||||
/// Returns a pointer to the Exit Button Widget.
|
||||
ImageButton* ExitButtonInstance();
|
||||
|
||||
@@ -127,7 +149,7 @@ namespace JUI {
|
||||
/// @see class Clickable.
|
||||
void OnRelease(const Vector2& m_pos, const MouseButton& m_btn, bool still_hovering) override;
|
||||
|
||||
/// @see class Hoverable.
|
||||
/// @see class Hoverable. v
|
||||
void OnHover(const J3ML::LinearAlgebra::Vector2 &MousePos) override
|
||||
{
|
||||
Hoverable::OnHover(MousePos);
|
||||
@@ -146,21 +168,36 @@ namespace JUI {
|
||||
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 (this->visible && this->focused)
|
||||
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;
|
||||
void Open();
|
||||
void Close();
|
||||
void SetOpen(bool value);
|
||||
void Toggle();
|
||||
[[nodiscard]] bool IsClosed() const;
|
||||
|
||||
virtual void Open();
|
||||
virtual void Close();
|
||||
virtual void SetOpen(bool value);
|
||||
virtual void Toggle();
|
||||
|
||||
protected:
|
||||
void UpdateInternalWidgetsTitlebarHeight();
|
||||
@@ -177,6 +214,7 @@ namespace JUI {
|
||||
bool open = false;
|
||||
//bool resizing = false;
|
||||
bool draggable = true;
|
||||
bool titlebar_hidden = false;
|
||||
bool dockable = false;
|
||||
int titlebar_height = Style::Window::TitlebarHeight;
|
||||
int title_font_size = 16;
|
||||
|
664
main.cpp
664
main.cpp
@@ -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>
|
||||
@@ -30,260 +34,542 @@
|
||||
#include <JUI/Widgets/Collapsible.hpp>
|
||||
#include <JUI/Widgets/CommandLine.hpp>
|
||||
|
||||
JUI::Scene* scene;
|
||||
JGL::Texture* sample_texture;
|
||||
JGL::Texture* slicer;
|
||||
JUI::VerticalListLayout* list;
|
||||
JUI::ScrollingRect* scroller;
|
||||
JUI::TextRect* widget_count;
|
||||
JUI::CommandLine* console;
|
||||
#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();
|
||||
}
|
||||
|
||||
JUI::Scene* CreateScene() {
|
||||
using namespace JUI;
|
||||
void CreateFileDialog() {
|
||||
auto* dialog = new JUI::FileDialogWindow(scene, ".");
|
||||
}
|
||||
|
||||
auto *root = new 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);
|
||||
|
||||
/*root->DescendantAdded += [&] (auto* node) {
|
||||
widget_count->SetContent("Widgets: " + count_descendants(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;
|
||||
};
|
||||
|
||||
root->DescendantRemoved += [&] (auto* node) {
|
||||
widget_count->SetContent("Widgets: " + count_descendants(root));
|
||||
};*/
|
||||
|
||||
auto* nineslice_demo_window = new JUI::Window(root);
|
||||
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->SetTitle("9-Slice Demo");
|
||||
nineslice_demo_window->Visible(false);
|
||||
nineslice_demo_window->TitlebarHeight(12);
|
||||
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;
|
||||
}
|
||||
|
||||
console = new JUI::CommandLine(root);
|
||||
console->Close();
|
||||
JUI::UtilityBar* CreateUtilityBar(JUI::Widget* root) {
|
||||
auto* topbar = new UtilityBar(root); {
|
||||
topbar->ZIndex(3);
|
||||
|
||||
JUI::UILogs.OnLog += [&] (const std::string& msg, Color4 c){ console->Log(msg, c);};
|
||||
// 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(); });
|
||||
|
||||
auto* topbar = new UtilityBar(root);
|
||||
topbar->ZIndex(3);
|
||||
auto* file = topbar->AddButton("Demos");
|
||||
demos->AddButton("Scroll Widget Demo", [&] {});
|
||||
|
||||
auto* file_tt = new JUI::Tooltip(file);
|
||||
file_tt->SetContent("Tooltip");
|
||||
demos->AddButton("Command Line", [&] {
|
||||
console->Toggle();
|
||||
});
|
||||
|
||||
file->OnClickEvent += [&, root, nineslice_demo_window] (Vector2 pos, JUI::MouseButton btn)
|
||||
{
|
||||
auto* ctx_menu = new ContextMenu(root);
|
||||
ctx_menu->Position(UDim2(0,20,0,0));
|
||||
auto* open_nineslice = ctx_menu->AddItem("9-Slice Widget Demo");
|
||||
demos->AddButton("File Dialog", [&] {CreateFileDialog();});
|
||||
|
||||
open_nineslice->OnClickEvent += [&, nineslice_demo_window] (Vector2 pos, JUI::MouseButton btn) {
|
||||
nineslice_demo_window->Visible(true);
|
||||
};
|
||||
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* open_scroll = ctx_menu->AddItem("Scroll Widget Demo");
|
||||
auto* ptA = new JUI::Anchor(topbar, {30_px, 40_px});
|
||||
|
||||
open_scroll->OnClickEvent += [&] (Vector2 pos, JUI::MouseButton btn) {};
|
||||
//ctx_menu->AddItem("");
|
||||
auto* ptB = new JUI::Anchor(demos, UDim2::BottomRight);
|
||||
|
||||
auto* line = new JUI::ArrowDecorator(topbar, ptB, ptA);
|
||||
|
||||
auto* open_console = ctx_menu->AddItem("Command Line");
|
||||
open_console->OnClickEvent += [&] (Vector2 pos, JUI::MouseButton btn)mutable {
|
||||
console->Open();
|
||||
};
|
||||
}
|
||||
|
||||
ctx_menu->ZIndex(3);
|
||||
};
|
||||
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;
|
||||
|
||||
topbar->AddButton("Edit");
|
||||
auto* view = topbar->AddButton("View");
|
||||
if (docked) {
|
||||
fps_graph->UndockGraph();
|
||||
docked = false;
|
||||
} else {
|
||||
fps_graph->DockGraph();
|
||||
docked = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
topbar->AddButton("Help");
|
||||
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->SetTextColor(Colors::Black);
|
||||
widget_count->BorderColor(Colors::Transparent);
|
||||
widget_count->BorderWidth(0);
|
||||
widget_count->Center();
|
||||
widget_count->AlignRight();
|
||||
});
|
||||
|
||||
//auto* horizontal = new HorizontalListLayout(root);
|
||||
//horizontal->ZIndex(1);
|
||||
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();
|
||||
|
||||
auto* column_rect = new Rect(root);
|
||||
}
|
||||
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);
|
||||
column_rect->Size({0, -24, 0.2f, 1.f});
|
||||
column_rect->Position({0, 24, 0, 0});
|
||||
column_rect->BGColor(Colors::Grays::Gainsboro);
|
||||
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(column_rect);
|
||||
|
||||
auto* btn_container1 = new Rect(column_layout);
|
||||
btn_container1->Size({100_percent, 24_px});
|
||||
auto* column_layout = new VerticalListLayout(widget_list);
|
||||
|
||||
auto* btn_h_layout1 = new HorizontalListLayout(btn_container1);
|
||||
btn_h_layout1->Padding({2_px});
|
||||
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* button = new TextButton(btn_h_layout1);
|
||||
//button->Position({5, 105, 0, 0});
|
||||
button->Size({0, 20, 0.32f, 0});
|
||||
button->SetTextColor(Colors::Black);
|
||||
button->SetContent("Button");
|
||||
button->AlignLeft();
|
||||
//button->Padding(5_px);
|
||||
auto* btn_container1 = new Rect(button_section_layout);
|
||||
{
|
||||
btn_container1->Size({100_percent, 32_px});
|
||||
|
||||
auto* tt2 = new JUI::Tooltip(button);
|
||||
tt2->SetContent("Test 123");
|
||||
|
||||
auto* button2 = new TextButton(btn_h_layout1);
|
||||
//button2->Position({5, 105, 0, 0});
|
||||
button2->Size({0, 20, 0.32f, 0});
|
||||
button2->SetTextColor(Colors::Black);
|
||||
button2->SetContent("Button");
|
||||
button2->AlignCenterHorizontally();
|
||||
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* button3 = new TextButton(btn_h_layout1);
|
||||
//button2->Position({5, 105, 0, 0});
|
||||
button3->Size({0, 20, 0.32f, 0});
|
||||
button3->SetTextColor(Colors::Black);
|
||||
button3->SetContent("Button");
|
||||
button3->AlignRight();
|
||||
auto* tt2 = new JUI::Tooltip(button);
|
||||
tt2->Content("Test 123");
|
||||
|
||||
auto* btn_container2 = new Rect(column_layout);
|
||||
btn_container2->Size({100_percent, 24_px});
|
||||
btn_container2->BGColor(Colors::DarkGray);
|
||||
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* btn_h_layout2 = new HorizontalListLayout(btn_container2);
|
||||
btn_h_layout2->Padding({2_px});
|
||||
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* button4 = new TextButton(btn_h_layout2);
|
||||
//button->Position({5, 105, 0, 0});
|
||||
button4->Size({0, 20, 0.32f, 0});
|
||||
button4->SetTextColor(Colors::Black);
|
||||
button4->SetContent("Button");
|
||||
button4->AlignLeft();
|
||||
//button4->CornerRounding(4);
|
||||
|
||||
auto* button5 = new TextButton(btn_h_layout2);
|
||||
//button2->Position({5, 105, 0, 0});
|
||||
button5->Size({0, 20, 0.32f, 0});
|
||||
button5->SetTextColor(Colors::Black);
|
||||
button5->SetContent("Button");
|
||||
button5->AlignCenterHorizontally();
|
||||
//button5->CornerRounding(4);
|
||||
auto* btn_container2 = new Rect(button_section_layout);
|
||||
{
|
||||
btn_container2->Size({100_percent, 32_px});
|
||||
btn_container2->BGColor(Colors::DarkGray);
|
||||
|
||||
auto* button6 = new TextButton(btn_h_layout2);
|
||||
//button2->Position({5, 105, 0, 0});
|
||||
button6->Size({0, 20, 0.32f, 0});
|
||||
button6->SetTextColor(Colors::Black);
|
||||
button6->SetContent("Button");
|
||||
button6->AlignRight();
|
||||
//button6->CornerRounding(4);
|
||||
|
||||
auto* checkbox_container = new Rect(column_layout);
|
||||
checkbox_container->Size({0, 24, 1, 0});
|
||||
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* checkbox_horiz = new HorizontalListLayout(checkbox_container);
|
||||
checkbox_horiz->Padding(2_px);
|
||||
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* label = new TextRect(checkbox_horiz);
|
||||
label->SetContent("Checkboxes");
|
||||
label->BorderWidth(0);
|
||||
label->AutoFitSizeToText(true);
|
||||
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* check1 = new Checkbox(checkbox_horiz);
|
||||
check1->Size({20_px, 20_px});
|
||||
auto* check2 = new Checkbox(checkbox_horiz);
|
||||
check2->Size({20_px, 20_px});
|
||||
check2->CheckedColor(Colors::Blue);
|
||||
check2->CornerRounding(7);
|
||||
auto* check3 = new Checkbox(checkbox_horiz);
|
||||
check3->Size({20_px, 20_px});
|
||||
check3->CheckedColor(Colors::Oranges::Coral);
|
||||
check3->CornerRounding(7);
|
||||
|
||||
auto* input_form = new TextInputForm(column_layout);
|
||||
input_form->Size({0,24, 1, 0});
|
||||
input_form->SetContent("");
|
||||
input_form->SetTextSize(14);
|
||||
|
||||
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* other_window = new JUI::Window(root);
|
||||
other_window->Position({10_percent, 10_percent});
|
||||
other_window->Size({30_percent, 25_percent});
|
||||
other_window->SetTitle("Another Window");
|
||||
auto* radio_btn_set = new Rect(collapsible->ContentBox());
|
||||
radio_btn_set->Size({100_percent, 20_px});
|
||||
|
||||
Tween* t = other_window->TweenPosition({50_percent, 50_percent}, {.time = 5});
|
||||
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(other_window->ViewportInstance());
|
||||
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);
|
||||
list->LayoutOrder(JUI::LayoutOrder::V::BOTTOM);*/
|
||||
|
||||
//nineslice_demo_window->Padding(1_px);
|
||||
// End Window //
|
||||
return scroller_demo;
|
||||
}
|
||||
|
||||
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}});
|
||||
JUI::Window* CreateAnimationDemoWindow() {
|
||||
|
||||
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)}});
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nineslice->CenterQuad({{96, 96}, {384-(96*2), 378-(96*2)}});
|
||||
JUI::Scene* CreateScene() {
|
||||
|
||||
auto darkie = new JUI::Image(nineslice_demo_window->ViewportInstance(), sample_texture);
|
||||
darkie->FitImageToParent(true);
|
||||
darkie->Color({255,255,255,128});
|
||||
auto *root = new Scene();
|
||||
|
||||
auto list = new VerticalListLayout(nineslice_demo_window->ViewportInstance());
|
||||
list->Padding(10_px);
|
||||
fps_graph = new JUI::FpsGraphWindow(root);
|
||||
|
||||
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());
|
||||
|
||||
topbar = CreateUtilityBar(root);
|
||||
|
||||
nineslice_demo_window = CreateNinesliceWindow(root);
|
||||
|
||||
// TODO: This instance will segfault on it's first call to be updated and I have **NO** idea why...
|
||||
//scroller_window = CreateScrollDemoWindow(root);
|
||||
|
||||
|
||||
CreateInfoboxWindow(root);
|
||||
|
||||
console = new JUI::CommandLine(root); {
|
||||
console->Close();
|
||||
JUI::UILogs.OnLog += [&] (const std::string& msg, Color4 c){ console->Log(msg, c);};
|
||||
}
|
||||
|
||||
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. Isn't that cool??");
|
||||
//b->FitText(true);
|
||||
b->Size({0, 20, 1, 0});
|
||||
b->Center();
|
||||
|
||||
root->SetViewportSize({800, 600});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
float scale = 1.f;
|
||||
float accum = 0;
|
||||
int iter = 0;
|
||||
|
||||
|
||||
class JUIDevelopmentTestWindow : public ReWindow::OpenGLWindow {
|
||||
public:
|
||||
@@ -302,27 +588,27 @@ public:
|
||||
void Update(float elapsed)
|
||||
{
|
||||
|
||||
widget_count->SetContent(std::format("Widgets: {}", count_descendants(scene)));
|
||||
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);
|
||||
/*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;
|
||||
scroller->canvas_height += text->GetAbsoluteSize().y;*/
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Draw()
|
||||
@@ -342,6 +628,9 @@ public:
|
||||
glLoadIdentity();
|
||||
Draw();
|
||||
|
||||
if (!focused)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(48));
|
||||
|
||||
this->SwapBuffers();
|
||||
}
|
||||
|
||||
@@ -378,17 +667,16 @@ public:
|
||||
}
|
||||
|
||||
void OnMouseWheel(const ReWindow::MouseWheelEvent &w) override {
|
||||
scale += w.WheelMovement * 0.125f;
|
||||
ui_scale += w.WheelMovement * 0.125f;
|
||||
|
||||
if (scene->ObserveMouseWheel(w.WheelMovement))
|
||||
return;
|
||||
|
||||
/// As a demo, change the scene's UI scale.
|
||||
scene->GlobalUIScale({scale, scale});
|
||||
scene->GlobalUIScale({ui_scale, ui_scale});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void inspect_widget(JUI::Widget* w, int depth = 1) {
|
||||
std::cout << std::setw(depth*4);
|
||||
std::cout << w->Name() << std::endl;
|
||||
@@ -400,8 +688,6 @@ void inspect_widget(JUI::Widget* w, int depth = 1) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main() {
|
||||
using namespace ReWindow;
|
||||
// TODO: Find out new jlog api for silencing specific loggers.
|
||||
|
@@ -35,22 +35,31 @@ 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;}
|
||||
|
||||
|
@@ -34,32 +34,54 @@ namespace JUI {
|
||||
|
||||
void RectBase::Draw(const Color4& bgColor, const Color4& borderColor, const Vector2 &abs_pos, const Vector2 &abs_size) {
|
||||
|
||||
// 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);
|
||||
RectBase::Draw(bgColor, borderColor, abs_pos, abs_size, corner_rounding_radius, border_width, border_mode, corner_mode);
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
|
||||
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) {
|
||||
border_mode = mode;
|
||||
}
|
||||
@@ -70,6 +92,7 @@ namespace JUI {
|
||||
corner_rounding_radius = radius;
|
||||
}
|
||||
|
||||
|
||||
float RectBase::CornerRounding() const { return corner_rounding_radius; }
|
||||
|
||||
|
||||
|
@@ -2,45 +2,43 @@
|
||||
#include <JGL/JGL.h>
|
||||
using namespace JUI;
|
||||
|
||||
void TextBase::SetContent(const std::string& content) { this->content = content; state_redraw = true; }
|
||||
void TextBase::Content(const std::string& content) { this->content = content; state_redraw = true; }
|
||||
|
||||
void TextBase::SetTextColor(const Color4& color) { this->text_color = color; state_redraw = true; }
|
||||
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::Align(const TextAlign::H& h_align, const TextAlign::V& v_align) {
|
||||
HorizontalTextAlign(h_align);
|
||||
VerticalTextAlign(v_align);
|
||||
}
|
||||
|
||||
void TextBase::AlignLeft() { SetHorizontalTextAlign(TextAlign::H::Left); }
|
||||
void TextBase::AlignLeft() { HorizontalTextAlign(TextAlign::H::Left); }
|
||||
|
||||
void TextBase::AlignRight() { SetHorizontalTextAlign(TextAlign::H::Right); }
|
||||
void TextBase::AlignRight() { HorizontalTextAlign(TextAlign::H::Right); }
|
||||
|
||||
void TextBase::AlignTop() { SetVerticalTextAlign(TextAlign::V::Top); }
|
||||
void TextBase::AlignTop() { VerticalTextAlign(TextAlign::V::Top); }
|
||||
|
||||
void TextBase::AlignBottom() { SetVerticalTextAlign(TextAlign::V::Bottom); }
|
||||
void TextBase::AlignBottom() { VerticalTextAlign(TextAlign::V::Bottom); }
|
||||
|
||||
void TextBase::AlignCenterBoth() {
|
||||
SetVerticalTextAlign(TextAlign::V::Center);
|
||||
SetHorizontalTextAlign(TextAlign::H::Center);
|
||||
VerticalTextAlign(TextAlign::V::Center);
|
||||
HorizontalTextAlign(TextAlign::H::Center);
|
||||
}
|
||||
|
||||
void TextBase::Center() { AlignCenterBoth(); }
|
||||
|
||||
void TextBase::AlignCenterHorizontally() {
|
||||
SetHorizontalTextAlign(TextAlign::H::Center);
|
||||
HorizontalTextAlign(TextAlign::H::Center);
|
||||
}
|
||||
|
||||
void TextBase::AlignCenterVertically() {
|
||||
SetVerticalTextAlign(TextAlign::V::Center);
|
||||
VerticalTextAlign(TextAlign::V::Center);
|
||||
}
|
||||
|
||||
void TextBase::SetWordWrap(bool wrap) { word_wrap = wrap; }
|
||||
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.
|
||||
@@ -50,19 +48,13 @@ void TextBase::Draw(const Vector2& abs_pos, const Vector2& abs_size, const std::
|
||||
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 = this->GetFont().MeasureString(content, size);
|
||||
auto bounds = TextStyler::Font().MeasureString(content, size);
|
||||
// Do nothing if there is no text.
|
||||
if (bounds.x == 0 || bounds.y == 0)
|
||||
return;
|
||||
@@ -95,8 +87,9 @@ void TextBase::Draw(const Vector2& abs_pos, const Vector2& abs_size, const std::
|
||||
// if Render Target needs updating.
|
||||
if (state_redraw) {
|
||||
text_canvas->Resize({(int) bounds.x, (int) bounds.y});
|
||||
J2D::Begin(text_canvas, true);
|
||||
J2D::DrawString(color, content, 0, 0, scale, size, this->set_font);
|
||||
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;
|
||||
}
|
||||
@@ -107,37 +100,39 @@ void TextBase::Draw(const Vector2& abs_pos, const Vector2& abs_size, const std::
|
||||
//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, scale, size, this->set_font);
|
||||
: 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::GetContent() const { return content; }
|
||||
std::string TextBase::Content() const { return content; }
|
||||
|
||||
Color4 TextBase::GetTextColor() const { return text_color; }
|
||||
|
||||
Color4 TextBase::GetOutlineColor() const { return outline_color; }
|
||||
Color4 TextBase::OutlineColor() const { return outline_color; }
|
||||
|
||||
TextAlign::H TextBase::GetHorizontalTextAlign() const { return h_align; }
|
||||
TextAlign::H TextBase::HorizontalTextAlign() const { return h_align; }
|
||||
|
||||
TextAlign::V TextBase::GetVerticalTextAlign() const { return v_align; }
|
||||
TextAlign::V TextBase::VerticalTextAlign() const { return v_align; }
|
||||
|
||||
Vector2 TextBase::GetTextBounds() { return GetFont().MeasureString(content, text_size); }
|
||||
Vector2 TextBase::TextBounds() { return TextStyler::Font().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;
|
||||
void TextBase::Font(const JGL::Font& font) {
|
||||
TextStyler::Font(font);
|
||||
state_redraw = true;
|
||||
}
|
||||
|
||||
void TextBase::SetTextSize(u32 size) {
|
||||
text_size = size;
|
||||
void TextBase::TextSize(int size) {
|
||||
TextStyler::TextSize(size);
|
||||
state_redraw = true;
|
||||
}
|
||||
|
||||
void TextBase::TextColor(const Color4& color) {
|
||||
TextStyler::TextColor(color);
|
||||
state_redraw = true;
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,7 @@ Widget::Widget() {
|
||||
name = "Widget";
|
||||
children = std::vector<Widget *>();
|
||||
}
|
||||
Widget::Widget(Widget* parent) : Widget() {
|
||||
Widget::Widget(Widget* parent) : Widget() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
@@ -147,6 +147,10 @@ float Widget::GetAbsoluteRotation() const {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
Vector2 Widget::GetAbsoluteCentroid() const {
|
||||
return GetAbsolutePosition() + (GetAbsoluteSize() / 2.f);
|
||||
}
|
||||
|
||||
std::vector<Widget *> Widget::GetChildren() { return children; }
|
||||
|
||||
std::vector<Widget *> Widget::GetDescendants() {
|
||||
@@ -281,6 +285,9 @@ void Widget::UpdateTweens(float elapsed) {
|
||||
}
|
||||
|
||||
void Widget::Update(float delta) {
|
||||
|
||||
is_mouse_inside = ComputeIsMouseInside();
|
||||
|
||||
UpdateChildWidgets(delta);
|
||||
UpdateTweens(delta);
|
||||
}
|
||||
@@ -292,13 +299,16 @@ struct {
|
||||
zIndexSort;
|
||||
|
||||
void Widget::DrawChildWidgets() {
|
||||
std::sort(children.begin(), children.end(), zIndexSort);
|
||||
for (auto child : children)
|
||||
if (z_dirty)
|
||||
z_dirty = false,
|
||||
std::sort(children.begin(), children.end(), zIndexSort);
|
||||
|
||||
for (auto* child : children)
|
||||
child->Draw();
|
||||
}
|
||||
|
||||
void Widget::UpdateChildWidgets(float delta) {
|
||||
for (auto child : children)
|
||||
for (auto* child : children)
|
||||
child->Update(delta);
|
||||
}
|
||||
|
||||
@@ -310,7 +320,8 @@ Widget* Widget::GetFamilyTreeRoot() const {
|
||||
return parent->GetFamilyTreeRoot();
|
||||
}
|
||||
|
||||
bool Widget::IsMouseInside() const {
|
||||
|
||||
bool Widget::ComputeIsMouseInside() const {
|
||||
float x = last_known_mouse_pos.x;
|
||||
float y = last_known_mouse_pos.y;
|
||||
auto pos = GetAbsolutePosition();
|
||||
@@ -322,6 +333,43 @@ bool Widget::IsMouseInside() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Widget::IsMouseInside() const {
|
||||
return is_mouse_inside;
|
||||
|
||||
/*float x = last_known_mouse_pos.x;
|
||||
float y = last_known_mouse_pos.y;
|
||||
auto pos = GetAbsolutePosition();
|
||||
auto size = GetAbsoluteSize();
|
||||
|
||||
if (x > pos.x && y > pos.y && x < pos.x + size.x && y < pos.y + size.y)
|
||||
return true;
|
||||
|
||||
return false;*/
|
||||
}
|
||||
|
||||
bool Widget::IsMouseInsideChildren(bool include_this) const {
|
||||
if (include_this)
|
||||
if (IsMouseInside())
|
||||
return true;
|
||||
|
||||
for (auto* child : children) {
|
||||
if (child->IsMouseInside())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Widget::IsMouseInsideDescendants(bool include_this) const {
|
||||
if (include_this)
|
||||
if (IsMouseInside())
|
||||
return true;
|
||||
for (auto* child : children) {
|
||||
if (child->IsMouseInsideDescendants(false))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Widget::ObserveMouseInput(MouseButton btn, bool pressed) {
|
||||
mbtn = btn;
|
||||
mb_state = pressed;
|
||||
@@ -396,7 +444,11 @@ void Widget::AnchorPoint(const Vector2& point) {
|
||||
|
||||
int Widget::ZIndex() const { return zindex;}
|
||||
|
||||
void Widget::ZIndex(int new_zindex) { zindex = new_zindex; }
|
||||
void Widget::ZIndex(int new_zindex) {
|
||||
zindex = new_zindex;
|
||||
if (parent)
|
||||
parent->z_dirty = true;
|
||||
}
|
||||
|
||||
AABB2D Widget::GetActualRenderBounds() const {
|
||||
return {GetAbsolutePosition(), GetAbsoluteSize()};
|
||||
@@ -405,7 +457,7 @@ AABB2D Widget::GetActualRenderBounds() const {
|
||||
void Widget::SetViewportSize(const Vector2 &vps) {
|
||||
viewport_size = vps;
|
||||
|
||||
for(auto& child : children)
|
||||
for(auto* child : children)
|
||||
child->SetViewportSize(viewport_size);
|
||||
}
|
||||
|
||||
@@ -629,4 +681,19 @@ float Widget::ComputeElementPadding(float size, const UDim &padding) {
|
||||
return padding.Pixels + (padding.Scale * size);
|
||||
}
|
||||
|
||||
Widget* Widget::GetHighestNonRootAncestor() {
|
||||
if (!parent)
|
||||
return nullptr;
|
||||
|
||||
Widget* previous = this;
|
||||
Widget* current = parent;
|
||||
|
||||
while (current->parent) {
|
||||
previous = current;
|
||||
current = current->parent;
|
||||
}
|
||||
|
||||
return previous;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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) {
|
||||
|
3
src/JUI/Mixins/Focusable.cpp
Normal file
3
src/JUI/Mixins/Focusable.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include <JUI/Mixins/Focusable.hpp>
|
||||
|
||||
bool JUI::Focusable::Focused() const { return focused; }
|
@@ -10,8 +10,26 @@ void JUI::Hoverable::OnExit(const Vector2 &MousePos) {
|
||||
//tooltip_threshold = 0;
|
||||
}
|
||||
|
||||
void JUI::Hoverable::Update(const Vector2 &m_pos, float delta) {
|
||||
|
||||
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()) {
|
||||
@@ -24,12 +42,12 @@ void JUI::Hoverable::Update(const Vector2 &m_pos, float delta) {
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
21
src/JUI/Mixins/Pinnable.cpp
Normal file
21
src/JUI/Mixins/Pinnable.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
25
src/JUI/Style/TextStyler.cpp
Normal file
25
src/JUI/Style/TextStyler.cpp
Normal 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);
|
||||
}
|
@@ -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};
|
||||
}
|
||||
|
@@ -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};
|
||||
|
@@ -46,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.
|
||||
@@ -161,7 +160,7 @@ namespace JUI {
|
||||
BaseBGColor(base);
|
||||
HoveredBGColor(hover);
|
||||
PressedBGColor(pressed);
|
||||
DisabledBGColor(disabled);\
|
||||
DisabledBGColor(disabled);
|
||||
UpdateVisualState();
|
||||
}
|
||||
}
|
@@ -22,12 +22,14 @@ namespace JUI {
|
||||
|
||||
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, check_color, padded_pos, padded_size);
|
||||
RectBase::Draw(check_color, border_color, padded_pos, padded_size);
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,7 +6,7 @@ namespace JUI {
|
||||
|
||||
header = new TextButton(this);
|
||||
header->Size({100_percent, header_height});
|
||||
header->SetContent("Collapsible");
|
||||
header->Content("Collapsible");
|
||||
header->BaseBGColor(Colors::DarkGray);
|
||||
header->BGColor(Colors::DarkGray);
|
||||
header->Center();
|
||||
@@ -21,7 +21,7 @@ namespace JUI {
|
||||
lil_arrow = new TextRect(header);
|
||||
lil_arrow->Size({header_height, header_height});
|
||||
lil_arrow->BGColor(Colors::Transparent);
|
||||
lil_arrow->SetContent("/\\");
|
||||
lil_arrow->Content("/\\");
|
||||
lil_arrow->BorderWidth(0);
|
||||
|
||||
content_box = new Rect(this);
|
||||
@@ -35,7 +35,7 @@ namespace JUI {
|
||||
|
||||
void Collapsible::Collapse() {
|
||||
//Size(saved_size);
|
||||
lil_arrow->SetContent("\\/");
|
||||
lil_arrow->Content("\\/");
|
||||
content_box->Visible(false);
|
||||
if (resize_anim != nullptr && !resize_anim->HasCompleted()) {
|
||||
resize_anim->Cancel();
|
||||
@@ -50,7 +50,7 @@ namespace JUI {
|
||||
}
|
||||
|
||||
void Collapsible::Expand() {
|
||||
lil_arrow->SetContent("/\\");
|
||||
lil_arrow->Content("/\\");
|
||||
content_box->Visible(true);
|
||||
if (resize_anim != nullptr && !resize_anim->HasCompleted())
|
||||
resize_anim->Cancel();
|
||||
@@ -77,8 +77,8 @@ namespace JUI {
|
||||
void Collapsible::HeaderHeight(const UDim &value) { header_height = value; }
|
||||
|
||||
void Collapsible::Title(const std::string &value) {
|
||||
header->SetContent(value);
|
||||
header->Content(value);
|
||||
}
|
||||
|
||||
std::string Collapsible::Title() const { return header->GetContent(); }
|
||||
std::string Collapsible::Title() const { return header->Content(); }
|
||||
}
|
134
src/JUI/Widgets/ColorPicker.cpp
Normal file
134
src/JUI/Widgets/ColorPicker.cpp
Normal 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)));
|
||||
}
|
@@ -17,7 +17,7 @@ namespace JUI
|
||||
}
|
||||
|
||||
CommandLine::CommandLine() {
|
||||
this->SetTitle("Console");
|
||||
this->Title("Console");
|
||||
|
||||
message_log_box = new JUI::ScrollingRect(this->ViewportInstance());
|
||||
message_log_box->Size(JUI::UDim2(0, -input_form_height, 1, 1));
|
||||
@@ -28,13 +28,14 @@ namespace JUI
|
||||
input_box = new JUI::TextInputForm(this->ViewportInstance());
|
||||
|
||||
input_box->SetAutoCompleteText(">Type Commands Here");
|
||||
input_box->SetContent("");
|
||||
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->SetTextSize(16);
|
||||
input_box->SetTextColor(Colors::White);
|
||||
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);
|
||||
@@ -46,17 +47,10 @@ namespace JUI
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
bool CommandLine::Opened() const { return this->open == true;}
|
||||
|
||||
bool CommandLine::Closed() const { return this->open == false;}
|
||||
|
||||
void CommandLine::Open(bool openVal) { SetOpen(openVal); }
|
||||
|
||||
void CommandLine::Close(bool openVal) { SetOpen(openVal);}
|
||||
|
||||
void CommandLine::SetOpen(bool openVal) {
|
||||
this->Visible(openVal);
|
||||
this->open = openVal;
|
||||
Window::SetOpen(openVal);
|
||||
this->input_box->SetFocused(openVal);
|
||||
}
|
||||
|
||||
void CommandLine::Log(const std::string &message, const Color4 &color) {
|
||||
@@ -65,11 +59,13 @@ namespace JUI
|
||||
|
||||
auto* entry = new JUI::TextRect(message_log_list);
|
||||
entry->Size({100_percent, UDim(message_height, 0)});
|
||||
entry->SetContent(message);
|
||||
entry->Content(message);
|
||||
entry->LayoutOrder(index++);
|
||||
entry->BGColor(index%2 == 0 ? light : dark);
|
||||
entry->SetTextColor(color);
|
||||
entry->SetTextSize(14);
|
||||
entry->TextColor(color);
|
||||
entry->TextSize(14);
|
||||
|
||||
message_log_box->canvas_height += entry->GetAbsoluteSize().y;
|
||||
}
|
||||
|
||||
bool CommandLine::ObserveKeyInput(Key key, bool pressed) {
|
||||
@@ -81,7 +77,7 @@ namespace JUI
|
||||
|
||||
void CommandLine::OnInputBoxSend(const std::string &message) {
|
||||
OnInput.Invoke(message);
|
||||
input_box->SetContent("");
|
||||
input_box->Content("");
|
||||
input_box->SetAutoCompleteText("");
|
||||
}
|
||||
}
|
||||
|
146
src/JUI/Widgets/ContextMenu.cpp
Normal file
146
src/JUI/Widgets/ContextMenu.cpp
Normal 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();
|
||||
}
|
||||
}
|
126
src/JUI/Widgets/FpsGraph.cpp
Normal file
126
src/JUI/Widgets/FpsGraph.cpp
Normal 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);
|
||||
}
|
||||
}
|
26
src/JUI/Widgets/LabeledSlider.cpp
Normal file
26
src/JUI/Widgets/LabeledSlider.cpp
Normal 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);
|
||||
}
|
@@ -5,7 +5,9 @@ namespace JUI
|
||||
ListLayout::ListLayout() : LayoutContainer() {
|
||||
Name("ListLayout");
|
||||
}
|
||||
ListLayout::ListLayout(Widget* parent) : ListLayout() {}
|
||||
ListLayout::ListLayout(Widget* parent) : ListLayout() {
|
||||
Parent(parent);
|
||||
}
|
||||
|
||||
VerticalListLayout::VerticalListLayout() : ListLayout() {}
|
||||
VerticalListLayout::VerticalListLayout(Widget* parent) : VerticalListLayout() {
|
||||
|
@@ -47,54 +47,54 @@ namespace JUI {
|
||||
// Draw Top-Left Quad.
|
||||
Vector2 tl_computed_pos = abs_pos;
|
||||
auto tl_quad = TopLeftQuad();
|
||||
JGL::J2D::DrawPartialSprite(texture, tl_computed_pos, tl_quad.minPoint, tl_quad.maxPoint);
|
||||
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::DrawPartialSprite(texture, tr_computed_pos, tr_quad.minPoint, tr_quad.maxPoint);
|
||||
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::DrawPartialSprite(texture, bl_computed_pos, bl_quad.minPoint, bl_quad.maxPoint);
|
||||
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::DrawPartialSprite(texture, br_computed_pos, br_quad.minPoint, 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::DrawPartialSprite(texture, t_computed_pos, t_quad.minPoint, t_quad.maxPoint, 0, {0,0}, {t_scaling, 1});
|
||||
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::DrawPartialSprite(texture, b_computed_pos, b_quad.minPoint, b_quad.maxPoint, 0, {0,0}, {b_scaling, 1});
|
||||
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::DrawPartialSprite(texture, l_computed_pos, l_quad.minPoint, l_quad.maxPoint, 0, {0, 0}, {1, l_scaling});
|
||||
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::DrawPartialSprite(texture, r_computed_pos, r_quad.minPoint, r_quad.maxPoint, 0, {0,0}, {1, r_scaling});
|
||||
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::DrawPartialSprite(texture, c_computed_pos, c_quad.minPoint, c_quad.maxPoint, 0, {0,0}, c_scaling);
|
||||
JGL::J2D::DrawPartialRenderTarget(texture, c_computed_pos, c_quad.minPoint, c_quad.maxPoint, 0, {0,0}, c_scaling);
|
||||
}
|
||||
|
||||
NineSliceRect::NineSliceRect() : Rect(), ImageBase() {
|
||||
|
@@ -21,6 +21,7 @@ namespace JUI {
|
||||
|
||||
void Scene::Update(float delta) {
|
||||
Widget::Update(delta);
|
||||
|
||||
}
|
||||
|
||||
void Scene::GlobalUIScale(const Vector2 &value) { ui_scale = value;}
|
||||
|
@@ -65,7 +65,7 @@ Vector2 ScrollingRect::CanvasPosition() const {
|
||||
// Wouldn't inner draw actually render everything onto the RenderTarget?
|
||||
void ScrollingRect::InnerDraw() {
|
||||
|
||||
J2D::Begin(canvas, true);
|
||||
J2D::Begin(canvas, nullptr, true);
|
||||
auto saved_pos = position;
|
||||
auto saved_size = size;
|
||||
|
||||
@@ -95,12 +95,13 @@ const Vector2i default_initialize_canvas_size {1024, 4096};
|
||||
|
||||
ScrollingRect::~ScrollingRect() { delete canvas; }
|
||||
|
||||
ScrollingRect::ScrollingRect() : canvas(new RenderTarget(default_initialize_canvas_size)) {
|
||||
ScrollingRect::ScrollingRect() : Rect(), canvas(new RenderTarget(default_initialize_canvas_size)) {
|
||||
Name("ScrollingRect");
|
||||
//bool success = canvas->SetMSAAEnabled(JGL::SampleRate::X8);
|
||||
}
|
||||
|
||||
ScrollingRect::ScrollingRect(Widget *parent) : Rect(parent), canvas(new RenderTarget(default_initialize_canvas_size)) {
|
||||
ScrollingRect::ScrollingRect(Widget *parent) : ScrollingRect() {
|
||||
Parent(parent);
|
||||
//bool success = canvas->SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE::MSAA_8X);
|
||||
}
|
||||
|
||||
@@ -118,29 +119,30 @@ bool ScrollingRect::ObserveKeyInput(Key key, bool pressed) {
|
||||
if (Widget::ObserveKeyInput(key, pressed))
|
||||
return true;
|
||||
|
||||
if (IsMouseInside()) {
|
||||
if (key == Keys::UpArrow && pressed)
|
||||
{
|
||||
Scroll(-scroll_amount);
|
||||
return true;
|
||||
}
|
||||
if (key == Keys::DownArrow && pressed)
|
||||
{
|
||||
Scroll(scroll_amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == Keys::PageDown && pressed) {
|
||||
ScrollToBottom();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == Keys::PageUp && pressed) {
|
||||
ScrollToTop();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// TODO: Forego this in favor of mouse scrolling and clicking.
|
||||
|
||||
if (key == Keys::UpArrow && pressed)
|
||||
{
|
||||
Scroll(-scroll_amount);
|
||||
return true;
|
||||
}
|
||||
if (key == Keys::DownArrow && pressed)
|
||||
{
|
||||
Scroll(scroll_amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == Keys::PageDown && pressed) {
|
||||
ScrollToBottom();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == Keys::PageUp && pressed) {
|
||||
ScrollToTop();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
101
src/JUI/Widgets/Separator.cpp
Normal file
101
src/JUI/Widgets/Separator.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include <JUI/Widgets/Separator.hpp>
|
||||
|
||||
|
||||
namespace JUI {
|
||||
Separator::Separator(): Widget() {
|
||||
Name("Separator");
|
||||
}
|
||||
|
||||
void Separator::UpdateSize() {
|
||||
if (orientation == Orientation::Horizontal)
|
||||
Size({100_percent, spacing});
|
||||
|
||||
if (orientation == Orientation::Vertical)
|
||||
Size({spacing, 100_percent});
|
||||
}
|
||||
|
||||
Separator::Separator(Widget *parent, const enum JUI::Orientation &orientation, const UDim &spacing,
|
||||
const enum JUI::LineFillMode &line_mode, const Color4 &line_color, float line_thickness): Separator() {
|
||||
this->orientation = orientation;
|
||||
this->spacing = spacing;
|
||||
this->line_mode = line_mode;
|
||||
this->line_color = line_color;
|
||||
this->line_thickness = line_thickness;
|
||||
UpdateSize();
|
||||
Parent(parent);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Separator::Orient(const enum JUI::Orientation &orientation) {
|
||||
this->orientation = orientation;
|
||||
|
||||
UpdateSize();
|
||||
}
|
||||
|
||||
void Separator::LineFillMode(const enum JUI::LineFillMode &line_mode) {
|
||||
this->line_mode = line_mode;
|
||||
}
|
||||
|
||||
Color4 Separator::LineColor() const { return line_color; }
|
||||
|
||||
void Separator::LineColor(const Color4 &color) {
|
||||
line_color = color;
|
||||
}
|
||||
|
||||
float Separator::GetAbsoluteSpacing() const {
|
||||
if (parent == nullptr) {
|
||||
// TODO: This function should in theory not be called in this case.
|
||||
return 0.f;
|
||||
}
|
||||
auto abs_parent_size = parent->GetAbsoluteSize();
|
||||
if (orientation == ::JUI::Orientation::Horizontal) {
|
||||
return spacing.Pixels + (spacing.Scale * abs_parent_size.y);
|
||||
}
|
||||
|
||||
if (orientation == Orientation::Vertical) {
|
||||
return spacing.Pixels + (spacing.Scale * abs_parent_size.x);
|
||||
}
|
||||
|
||||
// This control path should never be reached.
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
void Separator::InnerDraw() {
|
||||
|
||||
Vector2 abs_pos = GetAbsolutePosition();
|
||||
Vector2 abs_size = GetAbsoluteSize();
|
||||
|
||||
Vector2 start = abs_pos;
|
||||
Vector2 end = abs_size;
|
||||
|
||||
// TODO: Factor in padding.
|
||||
|
||||
float spacing = GetAbsoluteSpacing();
|
||||
|
||||
if (orientation == Orientation::Horizontal) {
|
||||
start = abs_pos + Vector2(0, abs_size.y / 2.f);
|
||||
end = start + Vector2(abs_size.x, 0);
|
||||
}
|
||||
|
||||
if (orientation == Orientation::Vertical) {
|
||||
start = abs_pos + Vector2(abs_size.x / 2.f, 0);
|
||||
end = start + Vector2(0, abs_size.y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (line_mode == LineFillMode::Dotted)
|
||||
J2D::DrawDottedLine(line_color, start, end, line_spacing, line_thickness);
|
||||
|
||||
if (line_mode == LineFillMode::Dashed)
|
||||
J2D::DrawDashedLine(line_color, start, end, line_spacing, line_spacing, line_thickness);
|
||||
|
||||
if (line_mode == LineFillMode::Solid)
|
||||
JGL::J2D::DrawLine(line_color, start, end, line_thickness);
|
||||
}
|
||||
|
||||
enum Orientation Separator::Orientation() const { return orientation;}
|
||||
|
||||
enum LineFillMode Separator::LineFillMode() const { return line_mode;}
|
||||
}
|
@@ -4,6 +4,29 @@
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct Range {
|
||||
T min;
|
||||
T max;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T map(T value, T in_min, T in_max, T out_min, T out_max) {
|
||||
return (value - in_min) / (in_max - in_min) * (out_max - out_min) + out_min;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T map(T value, const Range<T>& in, const Range<T>& out) {
|
||||
return map(value, in.min, in.max, out.min, out.max);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
Slider::Slider(JUI::Widget *parent) : Slider()
|
||||
{
|
||||
this->Parent(parent);
|
||||
@@ -28,12 +51,8 @@ namespace JUI
|
||||
}
|
||||
|
||||
void Slider::Update(float delta) {
|
||||
bool selected = Rect::IsMouseInside();
|
||||
|
||||
|
||||
hovered = IsMouseInside();
|
||||
|
||||
Hoverable::Update(last_known_mouse_pos, delta);
|
||||
Hoverable::Update(Rect::IsMouseInside(), delta);
|
||||
|
||||
// TODO: This is duplicated between here and Window.cpp
|
||||
// Will be abstracted away into Clickable shortly.
|
||||
@@ -51,9 +70,10 @@ namespace JUI
|
||||
|
||||
if (dragging)
|
||||
{
|
||||
float range = maximum - minimum;
|
||||
Vector2 mouse = last_known_mouse_pos;
|
||||
|
||||
float scrubber_width = GetScrubberAbsoluteSize().x;
|
||||
|
||||
float slider_abs_left = this->GetAbsolutePosition().x;
|
||||
float slider_abs_right = (this->GetAbsolutePosition().x + this->GetAbsoluteSize().x) - scrubber_width;
|
||||
float slider_total_width = this->GetAbsoluteSize().x - scrubber_width;
|
||||
@@ -62,7 +82,7 @@ namespace JUI
|
||||
|
||||
float offset = scrubberP - slider_abs_left;
|
||||
|
||||
float percentage = offset / slider_total_width;
|
||||
percentage = offset / slider_total_width;
|
||||
|
||||
float translated = Math::Clamp(roundMultiple(percentage * Range(), interval), 0.f, Range());
|
||||
|
||||
@@ -72,22 +92,56 @@ namespace JUI
|
||||
ValueChanged(value);
|
||||
|
||||
current = value;
|
||||
//std::cout << "slider value: " << value << std::endl;
|
||||
}
|
||||
|
||||
// TODO there is probably a more elegant fix for this.
|
||||
// Fixes the slider not letting go - Redacted.
|
||||
Widget* target = parent ? GetHighestNonRootAncestor() : this;
|
||||
if (!target->IsMouseInside()) { dragging = false; mb_state = false; }
|
||||
|
||||
Rect::Update(delta);
|
||||
}
|
||||
|
||||
|
||||
/// @note Calls GetScrubberAbsoluteSize() to compute box's offset.
|
||||
Vector2 Slider::GetScrubberAbsolutePosition() const {
|
||||
|
||||
// TODO: Integrate padding.
|
||||
|
||||
float x_offset = percentage * (GetAbsoluteSize().x - GetScrubberAbsoluteSize().x);
|
||||
|
||||
float rect_half_height = (GetAbsoluteSize().y / 2.f);
|
||||
|
||||
float y_offset = rect_half_height - (GetScrubberAbsoluteSize().y / 2.f);
|
||||
|
||||
Vector2 scrub_rel_pos = {x_offset, y_offset};
|
||||
|
||||
return GetAbsolutePosition() + scrub_rel_pos;
|
||||
|
||||
}
|
||||
|
||||
Vector2 Slider::GetScrubberAbsoluteSize() const {
|
||||
Vector2 abs_size = GetAbsoluteSize();
|
||||
Vector2 scrub_size_px = scrubber_size.GetPixels();
|
||||
Vector2 scrub_size_scale = scrubber_size.GetScale();
|
||||
|
||||
// TODO: Implement Slider::ScrubberPadding();
|
||||
Vector2 padding = GetAbsolutePaddingBottomRight();
|
||||
|
||||
return scrub_size_px + (abs_size * scrub_size_scale) - padding;
|
||||
}
|
||||
|
||||
void Slider::InnerDraw() {
|
||||
Rect::InnerDraw();
|
||||
|
||||
Vector2 pos = GetScrubberAbsolutePosition();
|
||||
Vector2 size = GetScrubberAbsoluteSize();
|
||||
|
||||
RectBase::Draw(scrubber_color, border_color, pos, size, scrubber_rounding, border_width, border_mode, corner_mode);
|
||||
}
|
||||
|
||||
void Slider::Draw() {
|
||||
//scrubber->Draw();
|
||||
Rect::Draw();
|
||||
Vector2 pos = {
|
||||
current * (GetAbsoluteSize().x - scrubber_width) + GetAbsolutePosition().x
|
||||
,GetAbsolutePosition().y
|
||||
};
|
||||
/// TODO: Implement internal padding on scrubber element?
|
||||
//J2D::Begin();
|
||||
JGL::J2D::FillRect(scrubber_color, pos, {scrubber_width, GetAbsoluteSize().y});
|
||||
//J2D::End();
|
||||
}
|
||||
|
||||
float Slider::Minimum() const { return minimum; }
|
||||
@@ -96,11 +150,21 @@ namespace JUI
|
||||
|
||||
float Slider::Interval() const { return interval; }
|
||||
|
||||
float Slider::CurrentValue() const { return current; }
|
||||
float Slider::CurrentValue() const {
|
||||
return map(percentage, {0, 1}, {minimum, maximum});
|
||||
}
|
||||
|
||||
Color4 Slider::ScrubberColor() const { return scrubber_color; }
|
||||
|
||||
float Slider::ScrubberWidth() const { return scrubber_width; }
|
||||
float Slider::ScrubberWidth() const { return scrubber_size.X.Pixels; }
|
||||
|
||||
UDim2 Slider::ScrubberSize() const {
|
||||
return scrubber_size;
|
||||
}
|
||||
|
||||
float Slider::ScrubberRounding() const {
|
||||
return scrubber_rounding;
|
||||
}
|
||||
|
||||
float Slider::Range() const { return maximum - minimum; }
|
||||
|
||||
@@ -110,11 +174,22 @@ namespace JUI
|
||||
|
||||
void Slider::Interval(float inter) { interval = inter; }
|
||||
|
||||
void Slider::CurrentValue(float value) { current = value; }
|
||||
void Slider::CurrentValue(float value) {
|
||||
percentage = map(value, {minimum, maximum}, {0.f, 1.f});
|
||||
}
|
||||
|
||||
float Slider::Percentage() const { return percentage;}
|
||||
void Slider::Percentage(float value) { percentage = value; }
|
||||
|
||||
void Slider::ScrubberColor(const Color4 &color) { scrubber_color = color;}
|
||||
|
||||
void Slider::ScrubberWidth(float width) { scrubber_width = width;}
|
||||
void Slider::ScrubberWidth(float width) { scrubber_size.X.Pixels = width;}
|
||||
|
||||
void Slider::ScrubberSize(const UDim2 &size) {
|
||||
scrubber_size = size;
|
||||
}
|
||||
|
||||
void Slider::ScrubberRounding(float rounding) { scrubber_rounding = rounding;}
|
||||
|
||||
Slider::Slider() {
|
||||
Name("Slider");
|
||||
|
@@ -26,7 +26,8 @@ namespace JUI {
|
||||
//return {0, 0};
|
||||
}
|
||||
|
||||
void Text::Draw() {
|
||||
void Text::InnerDraw()
|
||||
{
|
||||
auto abs_pos = this->GetAbsolutePosition();
|
||||
auto abs_size = this->GetAbsoluteSize();
|
||||
|
||||
@@ -35,6 +36,9 @@ namespace JUI {
|
||||
|
||||
TextBase::Draw(abs_pos+pos_pad, abs_size-size_pad);
|
||||
|
||||
}
|
||||
|
||||
void Text::Draw() {
|
||||
Widget::Draw();
|
||||
}
|
||||
|
||||
|
@@ -31,20 +31,18 @@ namespace JUI {
|
||||
|
||||
bool TextInputForm::ObserveMouseInput(MouseButton btn, bool pressed) {
|
||||
|
||||
if (pressed && btn == MouseButton::Left) {
|
||||
if (!IsMouseInside() && focused && time_focused > 1.f) {
|
||||
OnDeselect.Invoke();
|
||||
focused = false;
|
||||
}
|
||||
|
||||
if (IsMouseInside()) {
|
||||
if (!focused) {
|
||||
OnSelect.Invoke();
|
||||
}
|
||||
focused = true;
|
||||
return true;
|
||||
} else {
|
||||
if (focused)
|
||||
OnDeselect.Invoke();
|
||||
focused = false;
|
||||
|
||||
if (IsMouseInside() && pressed && btn == MouseButton::Left) {
|
||||
if (!focused) {
|
||||
OnSelect.Invoke();
|
||||
}
|
||||
|
||||
focused = true;
|
||||
return true;
|
||||
}
|
||||
return Widget::ObserveMouseInput(btn, pressed);
|
||||
}
|
||||
@@ -87,6 +85,7 @@ namespace JUI {
|
||||
|
||||
// TODO: Make cursor actually blink
|
||||
if (focused) {
|
||||
time_focused += elapsed;
|
||||
cursor_blink_time += elapsed;
|
||||
|
||||
if (cursor_blink_time > 1.f)
|
||||
@@ -96,9 +95,9 @@ namespace JUI {
|
||||
std::string result = input_buffer;
|
||||
|
||||
result.insert(cursor_position, 1, '|');
|
||||
SetContent(result);
|
||||
Content(result);
|
||||
} else {
|
||||
SetContent(input_buffer);
|
||||
Content(input_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,12 +114,27 @@ namespace JUI {
|
||||
return s;
|
||||
}
|
||||
|
||||
void TextInputForm::MoveCursorLeft() {
|
||||
if (cursor_position > 0)
|
||||
cursor_position--;
|
||||
int TextInputForm::MoveCursorLeft(int chars) {
|
||||
if (cursor_position <= 0) return 0; // TODO: This may be redundant?
|
||||
|
||||
int available_moves = Math::Min<int>(chars, cursor_position);
|
||||
|
||||
cursor_position -= available_moves;
|
||||
|
||||
return available_moves;
|
||||
}
|
||||
|
||||
void TextInputForm::MoveCursorRight() {
|
||||
int TextInputForm::MoveCursorRight(int chars) {
|
||||
if (cursor_position >= CursorMaxPosition()) return 0;
|
||||
|
||||
int chars_until_end = CursorMaxPosition() - cursor_position;
|
||||
|
||||
int available_moves = Math::Min<int>(chars, chars_until_end);
|
||||
|
||||
cursor_position += available_moves;
|
||||
|
||||
return available_moves;
|
||||
|
||||
if (cursor_position < CursorMaxPosition()-1)
|
||||
cursor_position++;
|
||||
}
|
||||
@@ -137,6 +151,15 @@ namespace JUI {
|
||||
void TextInputForm::SendInput(bool clear_input) {
|
||||
OnReturn.Invoke(input_buffer);
|
||||
|
||||
if (keep_input_history) {
|
||||
if (history.size() == 0 || history[0] != input_buffer) {
|
||||
history.insert(history.begin(), input_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
history_index = -1;
|
||||
saved_input_buffer = "";
|
||||
|
||||
if (clear_input) {
|
||||
input_buffer = "";
|
||||
cursor_position = 0;
|
||||
@@ -159,11 +182,15 @@ namespace JUI {
|
||||
if (cursor_position > 0) {
|
||||
input_buffer = input_buffer.erase(cursor_position-1, 1);
|
||||
cursor_position--;
|
||||
|
||||
saved_input_buffer = input_buffer;
|
||||
}
|
||||
}
|
||||
void TextInputForm::PushStringToCurrentPlaceInInputBuffer(const std::string& snippet) {
|
||||
input_buffer = input_buffer.insert(cursor_position, snippet);
|
||||
cursor_position += snippet.length();
|
||||
|
||||
saved_input_buffer = input_buffer;
|
||||
}
|
||||
void TextInputForm::PushKeyToCurrentPlaceInInputBuffer(const Key& key) {
|
||||
std::string insertion = lowercase(key.Mnemonic);
|
||||
@@ -194,12 +221,30 @@ namespace JUI {
|
||||
}
|
||||
|
||||
if (key == Keys::LeftArrow) {
|
||||
MoveCursorLeft();
|
||||
int qty = 1;
|
||||
// TODO: Move Left until next space.
|
||||
if (InputService::IsKeyDown(Keys::LeftShift)) qty = 5;
|
||||
MoveCursorLeft(qty);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == Keys::RightArrow) {
|
||||
MoveCursorRight();
|
||||
int qty = 1;
|
||||
// TODO: Move Right until next space.
|
||||
if (InputService::IsKeyDown(Keys::LeftShift)) qty = 5;
|
||||
MoveCursorRight(qty);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == Keys::DownArrow) {
|
||||
if (keep_input_history)
|
||||
ShowNextHistory();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == Keys::UpArrow) {
|
||||
if (keep_input_history)
|
||||
ShowPrevHistory();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -218,11 +263,45 @@ namespace JUI {
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextInputForm::ShowNextHistory() {
|
||||
if (keep_input_history) {
|
||||
|
||||
if (history_index > -1)
|
||||
history_index--;
|
||||
|
||||
std::string result = "";
|
||||
if (history_index == -1)
|
||||
SetInputBuffer(saved_input_buffer);
|
||||
else
|
||||
SetInputBuffer(history[history_index]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void TextInputForm::ShowPrevHistory() {
|
||||
if (keep_input_history) {
|
||||
if (history_index == -1 && !history.empty() || history.size()-1 > history_index) {
|
||||
history_index++;
|
||||
}
|
||||
|
||||
std::string result = "";
|
||||
if (history_index == -1)
|
||||
SetInputBuffer(saved_input_buffer);
|
||||
else
|
||||
SetInputBuffer(history[history_index]);
|
||||
}
|
||||
}
|
||||
|
||||
bool TextInputForm::HasFocus() const { return focused; }
|
||||
|
||||
void TextInputForm::SetFocused(bool focused) { this->focused = focused;}
|
||||
void TextInputForm::SetFocused(bool focused) {
|
||||
this->focused = focused;
|
||||
if (focused) time_focused = 0;
|
||||
}
|
||||
|
||||
void TextInputForm::GrabFocus() { SetFocused(true); }
|
||||
void TextInputForm::GrabFocus() {
|
||||
SetFocused(true);
|
||||
}
|
||||
|
||||
void TextInputForm::DropFocus() { SetFocused(false); }
|
||||
|
||||
@@ -250,4 +329,13 @@ namespace JUI {
|
||||
blacklist.insert(value);
|
||||
}
|
||||
|
||||
bool TextInputForm::KeepInputHistory() const { return keep_input_history; }
|
||||
|
||||
void TextInputForm::KeepInputHistory(bool value) {
|
||||
keep_input_history = value;
|
||||
}
|
||||
|
||||
std::string TextInputForm::InputHistory(int index) const {
|
||||
return history[index];
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ namespace JUI {
|
||||
Vector2 abs_pos = this->GetAbsolutePosition();
|
||||
Vector2 abs_size = this->GetAbsoluteSize();
|
||||
|
||||
|
||||
// TODO: Apply this everywhere..
|
||||
Vector2 pad_shifts_pos_by = {
|
||||
pad_left.Pixels + (pad_left.Scale * abs_size.x),
|
||||
@@ -26,12 +27,12 @@ namespace JUI {
|
||||
pad_bottom.Pixels + (pad_right.Scale * abs_size.y)
|
||||
};
|
||||
|
||||
Vector2 size = this->set_font.MeasureString(this->content, this->text_size);
|
||||
size += pad_shrinks_size_by*2.f;
|
||||
Vector2 size = this->font.MeasureString(this->content, this->text_size);
|
||||
//size += pad_shrinks_size_by*2.f;
|
||||
|
||||
if (abs_size.x < size.x)
|
||||
//if (abs_size.x < size.x)
|
||||
this->size.X.Pixels = size.x;
|
||||
if (abs_size.y < size.y)
|
||||
//if (abs_size.y < size.y)
|
||||
this->size.Y.Pixels = size.y;
|
||||
|
||||
}
|
||||
|
@@ -1 +1,24 @@
|
||||
#include <JUI/Widgets/Tooltip.hpp>
|
||||
#include <JUI/Widgets/Tooltip.hpp>
|
||||
|
||||
void JUI::Tooltip::Update(float delta) {
|
||||
|
||||
if (attachment && attachment->IsMouseInside())
|
||||
{
|
||||
auto coords = InputService::GetMousePosition();
|
||||
|
||||
auto rel_pos = parent->GetAbsolutePosition();
|
||||
|
||||
auto rel_udim = UDim2::FromPixels(rel_pos.x, rel_pos.y);
|
||||
|
||||
auto anchor = AnchorPoint();
|
||||
|
||||
Position(UDim2::FromPixels(coords.x - rel_pos.x, coords.y - rel_pos.y - GetAbsoluteSize().y));
|
||||
Visible(true);
|
||||
} else
|
||||
Visible(false);
|
||||
|
||||
|
||||
TextRect::Update(delta);
|
||||
}
|
||||
|
||||
|
||||
|
107
src/JUI/Widgets/UtilityBar.cpp
Normal file
107
src/JUI/Widgets/UtilityBar.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include <JUI/Widgets/UtilityBar.hpp>
|
||||
|
||||
namespace JUI {
|
||||
UtilityBar::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->BorderWidth(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::UtilityBar(Widget *parent): UtilityBar() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
ContextMenu * UtilityBar::AddSubmenu(const std::string &name) {
|
||||
auto* btn = AddButton(name);
|
||||
|
||||
|
||||
auto* submenu = new JUI::ContextMenu(btn);
|
||||
//submenu->AnchorPoint({0, 1});
|
||||
// TODO: Fix AnchorPoint behavior!!
|
||||
submenu->Position({0_percent, 100_percent});
|
||||
submenu->Visible(false);
|
||||
|
||||
btn->OnClickEvent += [this, submenu] (auto a, auto b) mutable {
|
||||
if (this->IsVisible())
|
||||
submenu->Visible(true);
|
||||
};
|
||||
|
||||
submenus.push_back(submenu);
|
||||
|
||||
return submenu;
|
||||
}
|
||||
|
||||
TextButton * UtilityBar::AddButton(const std::string &name) {
|
||||
// TODO: Refactor this to actually be able to use a set-font later.
|
||||
auto str_width = Fonts::Jupiteroid.MeasureString(name, 14);
|
||||
|
||||
auto* btn = new TextButton(layout);
|
||||
btn->Font(Fonts::Jupiteroid);
|
||||
btn->TextSize(14);
|
||||
btn->TextColor(Colors::Black);
|
||||
btn->Size({static_cast<int>(str_width.x)+16, 0, 0, 1});
|
||||
btn->BorderWidth(0.f);
|
||||
btn->Content(name);
|
||||
|
||||
buttons.push_back(btn);
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
TextButton * UtilityBar::AddButton(const std::string &name, const std::function<void()> &callback) {
|
||||
auto* button = AddButton(name);
|
||||
|
||||
button->OnClickEvent += [this, button, callback] (auto a, auto b) mutable {
|
||||
if (this->IsVisible() && button->IsMouseInside())
|
||||
callback();
|
||||
};
|
||||
return button;
|
||||
}
|
||||
|
||||
Separator * UtilityBar::AddSeparator(const UDim& size) {
|
||||
auto* separator = new Separator(layout);
|
||||
|
||||
separator->Size({100_percent, size});
|
||||
|
||||
|
||||
return new Separator(layout);
|
||||
}
|
||||
|
||||
void UtilityBar::Font(const JGL::Font &value) {
|
||||
TextStyler::Font(value);
|
||||
for (auto* btn : buttons)
|
||||
btn->Font(value);
|
||||
|
||||
for (auto* submenu : submenus)
|
||||
submenu->Font(value);
|
||||
}
|
||||
|
||||
void UtilityBar::TextSize(int value) {
|
||||
TextStyler::TextSize(value);
|
||||
for (auto* btn : buttons)
|
||||
btn->TextSize(value);
|
||||
|
||||
for (auto* submenu : submenus)
|
||||
submenu->TextSize(value);
|
||||
}
|
||||
|
||||
void UtilityBar::TextColor(const Color4 &color) {
|
||||
TextStyler::TextColor(color);
|
||||
for (auto* btn : buttons)
|
||||
btn->TextColor(color);
|
||||
|
||||
for (auto* submenu : submenus)
|
||||
submenu->TextColor(color);
|
||||
}
|
||||
}
|
@@ -43,8 +43,8 @@ namespace JUI {
|
||||
|
||||
TitleLabel = new Text(Topbar);
|
||||
TitleLabel->Center();
|
||||
TitleLabel->SetContent(title);
|
||||
TitleLabel->SetTextSize(title_font_size);
|
||||
TitleLabel->Content(title);
|
||||
TitleLabel->TextSize(title_font_size);
|
||||
|
||||
// TODO: Pull out this circle image generation code, make a Circle widget.
|
||||
|
||||
@@ -55,24 +55,37 @@ namespace JUI {
|
||||
auto* exb_rt = new RenderTarget(exb_tex);
|
||||
|
||||
if (exb_rt->SetMSAAEnabled(JGL::SampleRate::X8)) {/* using msaa to make the circle nicer. */}
|
||||
std::array<Color4, 2> exb_circle_colors {
|
||||
|
||||
/*std::array<Color4, 2> exb_circle_colors {
|
||||
Colors::White,//Color4(168, 28, 28, 255),
|
||||
Colors::White//Color4(212, 25, 25, 255)
|
||||
};
|
||||
|
||||
std::array<Vector2, 2> exb_circle_positions {
|
||||
Vector2(exb_rt->GetDimensions()) / 2,
|
||||
Vector2(exb_rt->GetDimensions()) / 2
|
||||
};
|
||||
Vector2(exb_rt->GetDimensions()) / 2,
|
||||
Vector2(exb_rt->GetDimensions()) / 2
|
||||
};
|
||||
|
||||
std::array<float, 2> exb_circle_radii {
|
||||
(float) (exb_rt->GetDimensions().x * 0.33),
|
||||
(float) (exb_rt->GetDimensions().x * 0.28)
|
||||
};
|
||||
(float) (exb_rt->GetDimensions().x * 0.33),
|
||||
(float) (exb_rt->GetDimensions().x * 0.28)
|
||||
};*/
|
||||
|
||||
J2D::Begin(exb_rt, true);
|
||||
J2D::BatchFillCircle(exb_circle_colors.data(), exb_circle_positions.data(), exb_circle_radii.data(), 24, 2);
|
||||
Instance2D exb_circle_data[2];
|
||||
exb_circle_data[0].color = Colors::White;
|
||||
exb_circle_data[1].color = Colors::White;
|
||||
exb_circle_data[0].position = Vector2(exb_rt->GetDimensions()) / 2.f;
|
||||
exb_circle_data[1].position = Vector2(exb_rt->GetDimensions()) / 2.f;
|
||||
exb_circle_data[0].scale = Vector2((float) (exb_rt->GetDimensions().x * 0.33));
|
||||
exb_circle_data[1].scale = Vector2((float) (exb_rt->GetDimensions().x * 0.28));
|
||||
|
||||
|
||||
|
||||
J2D::Begin(exb_rt, nullptr, true);
|
||||
J2D::BatchFillCircle(exb_circle_data, 24, 2);
|
||||
//J2D::BatchFillCircle(exb_circle_colors.data(), exb_circle_positions.data(), exb_circle_radii.data(), 24, 2);
|
||||
J2D::End();
|
||||
|
||||
delete exb_rt;
|
||||
|
||||
exit_btn = new ImageButton(Topbar);
|
||||
@@ -88,8 +101,8 @@ namespace JUI {
|
||||
exit_btn->HoveredImageColor(Colors::Red);
|
||||
exit_btn->PressedImageColor(Colors::Reds::DarkRed);
|
||||
|
||||
exit_btn->OnReleaseEvent += [&] (auto... _) {
|
||||
this->Visible(false);
|
||||
exit_btn->OnReleaseEvent += [&] (...) {
|
||||
this->Close();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -117,13 +130,15 @@ namespace JUI {
|
||||
|
||||
Rect *Window::ViewportInstance() { return Viewport; }
|
||||
|
||||
Rect * Window::Content() { return ViewportInstance(); }
|
||||
|
||||
void Window::SetDrag(bool d) {
|
||||
Draggable::SetDrag(d);
|
||||
this->initial_drag_offset = this->last_known_mouse_pos - this->GetAbsolutePosition();
|
||||
}
|
||||
|
||||
void Window::SetTitleFont(const Font& f) {
|
||||
TitleLabel->SetFont(f);
|
||||
TitleLabel->Font(f);
|
||||
//exit_btn->SetFont(f);
|
||||
}
|
||||
|
||||
@@ -144,13 +159,22 @@ namespace JUI {
|
||||
|
||||
void Window::OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &btn,
|
||||
bool still_hovering) {
|
||||
|
||||
// TODO: This ONLY invokes if the mouse is still indeed **hovering**
|
||||
// Some controls would like to listen in cases where it isn't.
|
||||
|
||||
Clickable::OnRelease(mouse_pos, btn, still_hovering);
|
||||
clicked = false;
|
||||
|
||||
|
||||
if (dragging)
|
||||
this->SetDrag(false);
|
||||
|
||||
if (resizing)
|
||||
|
||||
|
||||
if (resizing) {
|
||||
this->SetResize(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -191,9 +215,8 @@ namespace JUI {
|
||||
}
|
||||
|
||||
Widget::Update(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.
|
||||
@@ -202,7 +225,7 @@ namespace JUI {
|
||||
OnClick(last_known_mouse_pos, mbtn);
|
||||
}
|
||||
|
||||
if (IsClicked() && !mb_state)
|
||||
if (prev_mb_state && !mb_state)
|
||||
{
|
||||
OnRelease(last_known_mouse_pos, mbtn, IsHovered());
|
||||
}
|
||||
@@ -215,10 +238,13 @@ namespace JUI {
|
||||
return;
|
||||
RectBase::Draw(this->GetAbsolutePosition(), this->GetAbsoluteSize());
|
||||
Widget::Draw();
|
||||
|
||||
//J2D::DrawString(Colors::Black, std::format("hover: {}", hovered), this->GetAbsolutePosition().x, this->GetAbsolutePosition().y, 1.f, 10);
|
||||
//J2D::DrawString(Colors::Black, std::format("mbutton: {}", mb_state), this->GetAbsolutePosition().x, this->GetAbsolutePosition().y+10, 1.f, 10);
|
||||
}
|
||||
|
||||
void Window::SetTitle(const std::string &title) {
|
||||
TitleInstance()->SetContent(title);
|
||||
void Window::Title(const std::string &title) {
|
||||
TitleInstance()->Content(title);
|
||||
}
|
||||
|
||||
void Window::MinSize(const Vector2 &constraint) {
|
||||
@@ -289,8 +315,18 @@ namespace JUI {
|
||||
}
|
||||
|
||||
void Window::SetOpen(bool value) {
|
||||
bool value_changed = value != open;
|
||||
open = value;
|
||||
Visible(open);
|
||||
|
||||
Visible(value);
|
||||
|
||||
if (value_changed) {
|
||||
if (value)
|
||||
OnOpen.Invoke();
|
||||
else
|
||||
OnClose.Invoke();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Window::Toggle() {
|
||||
@@ -301,5 +337,25 @@ namespace JUI {
|
||||
|
||||
void Window::Open() { SetOpen(true); }
|
||||
|
||||
bool Window::ObserveMouseMovement(const Vector2 &latest_known_pos) {
|
||||
/// 1. If dragging or resizing, or check if any children are observing.
|
||||
|
||||
if (!this->visible && !this->focused)
|
||||
return false;
|
||||
|
||||
if (Widget::ObserveMouseMovement(latest_known_pos))
|
||||
return true;
|
||||
|
||||
if (dragging || resizing)
|
||||
return true;
|
||||
|
||||
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Window::IsOpen() const { return open;}
|
||||
}
|
||||
|
||||
bool Window::IsClosed() const { return !open;}
|
||||
}
|
||||
|
Reference in New Issue
Block a user