Compare commits
118 Commits
Prerelease
...
Prerelease
Author | SHA1 | Date | |
---|---|---|---|
c3e372ac29 | |||
fee9f2083c | |||
fdaf68b01e | |||
62cfd1bd58 | |||
b7ab9a8780 | |||
8d90f990f0 | |||
5ccbb84e55 | |||
74ae05321b | |||
906bde163a | |||
d0fcf9cce2 | |||
2e0ce74753 | |||
47a7b19648 | |||
80c28921b1 | |||
6166984daa | |||
8589431cc7 | |||
1092ef2b38 | |||
1a11fa824b | |||
1bd11c5235 | |||
956183bade | |||
86d01099e2 | |||
90272e35f4 | |||
e2528f54fb | |||
fffcc7f12d | |||
b3f65b3396 | |||
4193a2a693 | |||
ee0e92d826 | |||
86aa41a3db | |||
3486ee03d7 | |||
34257eb256 | |||
c4f88c9021 | |||
052157f484 | |||
bd02c91c45 | |||
73a461ab56 | |||
efb89dbcee | |||
e607db2ae2 | |||
c7d214c851 | |||
116c2e2e2f | |||
73ef647156 | |||
bdbfe61b87 | |||
f105053fb2 | |||
f772369686 | |||
9cf78742ae | |||
3aa9f419a2 | |||
c338cc66a4 | |||
64ee666a83 | |||
13cbaa733c | |||
ad2e3e9a45 | |||
24d5065f01 | |||
bfd898b4f6 | |||
2579b587c4 | |||
883eabb5e3 | |||
24e38b9ec3 | |||
1b2fdecc18 | |||
6e58e30022 | |||
030da85166 | |||
bd19b29a27 | |||
251daa4070 | |||
fc65e9d229 | |||
2aaba5c1f7 | |||
97edc67fad | |||
1053a32317 | |||
9a70839d2e | |||
578d15200c | |||
15a2bfe667 | |||
d204d991c1 | |||
29f5125ccc | |||
c44e5c830c | |||
c78f9a045c | |||
7f2477677a | |||
fe454209f7 | |||
5673ce3b9e | |||
02cd200114 | |||
32d9615001 | |||
5efbe3fea3 | |||
1520db8dcd | |||
663355726b | |||
83ffecb91d | |||
135a1564f2 | |||
d3f29e2072 | |||
5f7a2326bd | |||
8acf32f140 | |||
d89d74052c | |||
2e65380ae6 | |||
5bf6e2cabc | |||
593ba22626 | |||
27cde1f37c | |||
103813e9c1 | |||
0ef7ec2a7f | |||
0cd3fbfc17 | |||
e7e469864f | |||
0f00cfa48d | |||
c9a9c6aeb5 | |||
355e9f975e | |||
5293a845ad | |||
a31ee731d2 | |||
389f266722 | |||
|
e32bce6b50 | ||
93632faaed | |||
e11b8c8c50 | |||
2e0d75391e | |||
bc8ba3ed02 | |||
9867f49ebf | |||
c5766aa358 | |||
3d22d683bc | |||
432a5990ce | |||
2a7b562b8f | |||
936c1902d0 | |||
e8eb018959 | |||
465d7052b7 | |||
ff84118b10 | |||
9b3bf93ea1 | |||
0ffb750f82 | |||
423dc188cd | |||
3a062a2709 | |||
146b8190b0 | |||
1cb0c1299a | |||
3ad9c615b6 | |||
19e066c835 |
@@ -26,11 +26,23 @@ 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)
|
||||
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-7.2.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME Event
|
||||
URL https://git.redacted.cc/josh/Event/archive/Release-12.zip
|
||||
@@ -38,22 +50,17 @@ CPMAddPackage(
|
||||
|
||||
CPMAddPackage(
|
||||
NAME J3ML
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/Release-3.4.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME jlog
|
||||
URL https://git.redacted.cc/josh/jlog/archive/Prerelease-16.zip
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/3.4.5.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME ReWindow
|
||||
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-21.zip
|
||||
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-32.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME JGL
|
||||
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-38.zip
|
||||
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-55.zip
|
||||
)
|
||||
|
||||
target_include_directories(JUI PUBLIC ${Event_SOURCE_DIR}/include)
|
||||
@@ -65,7 +72,7 @@ target_include_directories(JUI PUBLIC ${ReWindow_SOURCE_DIR}/include)
|
||||
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})
|
||||
install(FILES ${JUI_HEADERS} DESTINATION include/${PROJECT_NAME})
|
||||
|
||||
target_link_libraries(JUI PUBLIC Event J3ML jlog ReWindowLibrary JGL)
|
||||
target_link_libraries(JUI PUBLIC Event J3ML jlog ReWindow JGL)
|
||||
|
||||
add_executable(RedactedJUIDemo main.cpp)
|
||||
target_link_libraries(RedactedJUIDemo PUBLIC JUI)
|
||||
|
60
README.md
60
README.md
@@ -2,33 +2,58 @@
|
||||
|
||||

|
||||
|
||||
Fourth Time's The Charm! (tm)
|
||||
Fourth Time's The Charm!™
|
||||
|
||||
JUI is a C++20 Library for building interactive menus in OpenGL / Redacted3D.
|
||||
#### JUI is a C++20 Library for building interactive menus in OpenGL / Redacted3D.
|
||||
|
||||
It is expressly built with our Redacted3D engine in mind, but steps have been taken to support OpenGL generally.
|
||||
|
||||

|
||||
|
||||
## Abstract
|
||||
|
||||
JUI provides a set of objects that we term Widgets. Widgets can be styled and laid out on-screen in relation to each other. Each widget has a single parent, and a list of child elements. Your root widget should be a Scene object.
|
||||
|
||||
Provided widgets include Scene, Rect, Text, TextRect, Button, TextButton, TextInputForms, Slider, Image, ImageRect, RadioButton
|
||||
JUI provides a set of objects that we term Widgets, which can be styled and laid out on-screen in relation to each other. Each widget has a single parent, and a list of child elements. Your root widget should be a Scene object.
|
||||
|
||||
## Features
|
||||
|
||||
### Why use this instead of imgui?
|
||||
|
||||
## Usage
|
||||
* Comprehensive list of common UI widgets:
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* Vertical and Horizontal ListLayout, GridLayout
|
||||
* Separator
|
||||
* ScrollRect
|
||||
* Extendable - Widgets can be extended via class derivation, and even combined to create complex behavior.
|
||||
* Low-overhead stateful GUI elements.
|
||||
* Easy integration with your project. Simply provide update, draw, and user-input callbacks to your scene.
|
||||
|
||||
## Examples
|
||||
|
||||

|
||||
|
||||
|
||||
Browse the src/Demos directories for examples of building and interacting with things in JUI.
|
||||
|
||||
Browse the Repository's , linked above, for further explainations and sample code on each widget.
|
||||
|
||||
## Dependencies
|
||||
|
||||
ReJUI shares dependencies with it's rendering layer, .
|
||||
Currently, the package is also integrated with  and .
|
||||
|
||||
|
||||
`Fedora/RHEL: dnf install cmake make gcc-g++ libX11 libX11-devel mesa-libGL-devel vulkan-loader-devel`
|
||||
|
||||
`Ubuntu/Debian: apt-get install cmake make gcc g++ libx11-6 libx11-dev libgl-dev libvulkan-dev libxrandr-dev`
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation is automatically generated from latest commit and is hosted at https://doc.redacted.cc/jui .
|
||||
@@ -37,17 +62,6 @@ Documentation is automatically generated from latest commit and is hosted at htt
|
||||
|
||||
Contributions to JUI are welcome! Feel free to file bug reports or feature requests by creating an Issue. Pull requests are also very welcome!
|
||||
|
||||
## History
|
||||
|
||||
JUI started out as my menu toolkit for the LOVE2D framework many years ago. Between then and now I had re-implemented it twice, once for MonoGame Framework, and again in C++ for SDL2. Legacy versions are listed below.
|
||||
|
||||
JUI v1 - LOVE2D / Lua
|
||||
|
||||
JUI v2 - MonoGame / C#
|
||||
|
||||
JUI v3 - SDL2 / C++
|
||||
|
||||
|
||||
## License
|
||||
|
||||
JUI is expressly released without a license, under no restrictions. We dedicate all of our works to the public domain for the (hopeful) betterment of humanity.
|
||||
|
BIN
assets/9slice.png
Normal file
BIN
assets/9slice.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
@@ -17,11 +17,11 @@
|
||||
|
||||
using J3ML::LinearAlgebra::Vector2;
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
namespace JUI {
|
||||
/// The ImageBase class is an object that handles managing and rendering a texture reference.
|
||||
/// This class is used as a mixin on widgets that must render images. i.e. Image class
|
||||
/// This class is used as a mixin on widgets that must render images. i.e. Image class.
|
||||
/// This object is complex, stateful, and manages resources. Do not use this as a general-purpose texture data type.
|
||||
/// @see JGL::Texture and JGL::RenderTarget for more suitable classes.
|
||||
class ImageBase
|
||||
{
|
||||
public:
|
||||
@@ -47,6 +47,9 @@ namespace JUI
|
||||
void Scale(const Vector2& newScale);
|
||||
void Origin(const Vector2& newOrigin);
|
||||
public:
|
||||
/// Draws the image at the given position, with the instances' scale and origin.
|
||||
void Draw(const Vector2& pos);
|
||||
/// Draws the image at the given pos, manually scaled to fit the given size.
|
||||
void Draw(const Vector2& pos, const Vector2& size);
|
||||
protected:
|
||||
JGL::Texture* texture;
|
||||
|
@@ -9,7 +9,6 @@
|
||||
/// @desc Base class for container objects. Container objects lay out widgets in a certain order.
|
||||
/// @edit 2024-08-02
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Base/Widget.hpp>
|
||||
|
@@ -15,10 +15,11 @@
|
||||
#include <Event.h>
|
||||
|
||||
#include <Color4.hpp>
|
||||
#include "JGL/types/RenderTarget.h"
|
||||
#include <JUI/DefaultStyle.hpp>
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
enum class BorderMode
|
||||
{
|
||||
Outline, /// As BorderWidth increases, the border grows outward. The dimensions of the widgets contents do not change.
|
||||
@@ -36,9 +37,9 @@ namespace JUI
|
||||
//Event<Vector2> MousePress;
|
||||
//Event<Vector2, bool> MouseRelease;
|
||||
public:
|
||||
void CornerRounding(float radius);
|
||||
virtual void CornerRounding(float radius);
|
||||
|
||||
float CornerRounding() const;
|
||||
[[nodiscard]] float CornerRounding() const;
|
||||
|
||||
// TODO: Implement per-corner rounding in JGL::Outline/FillRect
|
||||
//void CornerRounding(float tlRadius, float trRadius, float blRadius, float brRadius);
|
||||
@@ -50,13 +51,13 @@ namespace JUI
|
||||
void SetClipsDescendants(bool clipping);
|
||||
void BGColor(const Color4& col);
|
||||
void BorderColor(const Color4& col);
|
||||
void SetBorderWidth(float w);
|
||||
void BorderWidth(float w);
|
||||
|
||||
|
||||
Color4 BGColor() const;
|
||||
Color4 GetBorderColor() const;
|
||||
float GetBorderWidth() const;
|
||||
enum BorderMode BorderMode() const;
|
||||
[[nodiscard]] Color4 BGColor() const;
|
||||
[[nodiscard]] Color4 GetBorderColor() const;
|
||||
[[nodiscard]] float GetBorderWidth() const;
|
||||
[[nodiscard]] enum BorderMode BorderMode() const;
|
||||
|
||||
void BorderMode(const enum BorderMode& mode);
|
||||
|
||||
@@ -65,14 +66,15 @@ namespace JUI
|
||||
void SetBorderStyling(const Color4& color, float width);
|
||||
|
||||
void Draw(const Vector2& pos, const Vector2& size);
|
||||
|
||||
void Draw(const Color4& bgColor, const Color4& fgColor, const Vector2& pos, const Vector2& size);
|
||||
protected:
|
||||
|
||||
enum BorderMode border_mode = BorderMode::Middle;
|
||||
bool mouse_press_debounce;
|
||||
bool mouse_inside_debounce;
|
||||
float border_width = 1.f;
|
||||
Color4 bg_color = {128,128,128, 255};
|
||||
Color4 border_color = {192, 192, 192, 0};
|
||||
bool mouse_press_debounce{};
|
||||
bool mouse_inside_debounce{};
|
||||
float border_width = Style::BorderLineWidth;
|
||||
Color4 bg_color = Style::BackgroundColor;
|
||||
Color4 border_color = Style::BorderColor;
|
||||
bool clips_descendants = false; // Controls if child objects can render outside of their parent's rectangle bounds.
|
||||
float corner_rounding_radius = 0.f; // Curves the rectangle corners by N degrees.
|
||||
};
|
||||
|
@@ -1,23 +1,27 @@
|
||||
/// 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 TextBase.hpp
|
||||
/// @desc Demo Program Entry Point
|
||||
/// @edit 2024-11-16
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra.hpp>
|
||||
#include <Color4.hpp>
|
||||
#include <JGL/JGL.h>
|
||||
#include <JGL/types/Font.h>
|
||||
|
||||
using J3ML::LinearAlgebra::Vector2;
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
/// Enumerations for alignment of text within a desired "Box".
|
||||
namespace TextAlign {
|
||||
enum class H { Left, Center, Right }; // Horizontal
|
||||
enum class V { Top, Center, Bottom }; // Vertical
|
||||
}
|
||||
|
||||
namespace JUI {
|
||||
class TextBase;
|
||||
|
||||
/// How should a text widget behave when it's text runs out of room?
|
||||
enum class TextOverflowMode
|
||||
{
|
||||
enum class TextOverflowMode {
|
||||
WRAP, /// Wraps at the nearest 'word', or otherwise appropriate stop in text.
|
||||
WRAP_ANYWHERE, /// Wraps the text anywhere.
|
||||
TRUNCATE, /// Cuts the text off and suffixes '...' to indicate the lack of the full intended message.
|
||||
@@ -25,93 +29,104 @@ namespace JUI
|
||||
TRUNCATE_NO_DOTS, /// Cuts the text off at the first appropriate break in text.
|
||||
TRUNCATE_ANYWHERE_NO_DOTS /// Cuts the text off at any point in the string.
|
||||
};
|
||||
}
|
||||
|
||||
/// TextBase class, implements core mechanics of drawing text in JUI, and is used by Text Widget class.
|
||||
class TextBase {
|
||||
public:
|
||||
TextBase();
|
||||
/// Enumerations for alignment of text within a desired "Box".
|
||||
namespace JUI::TextAlign {
|
||||
// Horizontal
|
||||
enum class H { Left, Center, Right };
|
||||
// Vertical
|
||||
enum class V { Top, Center, Bottom };
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string GetContent() const;
|
||||
[[nodiscard]] Color4 GetTextColor() const;
|
||||
[[nodiscard]] Color4 GetOutlineColor() const;
|
||||
[[nodiscard]] TextAlign::H GetHorizontalTextAlign() const;
|
||||
[[nodiscard]] TextAlign::V GetVerticalTextAlign() const;
|
||||
/// TextBase class, implements core mechanics of drawing text in JUI, and is used by Text Widget class.
|
||||
class JUI::TextBase {
|
||||
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:
|
||||
// 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);
|
||||
|
||||
TextOverflowMode GetOverflowMode() const;
|
||||
/// 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);
|
||||
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;
|
||||
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);
|
||||
|
||||
/// Returns the
|
||||
[[nodiscard]] Vector2 GetTextBounds() const;
|
||||
[[nodiscard]] Vector2 GetTextPosition() const;
|
||||
[[nodiscard]] JGL::Font GetFont() const;
|
||||
[[nodiscard]] u32 GetTextSize() const;
|
||||
/// 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 SetOverflowMode(const TextOverflowMode& mode);
|
||||
/// 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 SetTextSize(u32 size);
|
||||
/// 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 SetFont(const JGL::Font& font);
|
||||
/// Aligns the text of this widget to the left-hand-side of the parent's bounding box.
|
||||
/// @see SetHorizontalTextAlign, TextAlign::H
|
||||
void AlignLeft();
|
||||
|
||||
void SetContent(const std::string& content);
|
||||
/// Aligns the text of this widget to the right-hand-side of the parent's bounding box.
|
||||
/// @see SetHorizontalTextAlign, TextAlign::H
|
||||
void AlignRight();
|
||||
|
||||
void SetTextColor(const Color4& color);
|
||||
/// Aligns the text of this widget to the top of the parent's bounding box.
|
||||
/// @see SetVerticalTextAlign, TextAlign::V
|
||||
void AlignTop();
|
||||
|
||||
void SetOutlineColor(const Color4& color);
|
||||
/// Aligns the text of this widget to the bottom of the parent's bounding box.
|
||||
/// @see SetVerticalTextAlign, TextAlign::V
|
||||
void AlignBottom();
|
||||
|
||||
/// Set the horizontal alignment of this text widget in relation to it's parent,
|
||||
/// while keeping the text retained inside the parent widget's bounds.
|
||||
/// @see TextAlign::H
|
||||
void SetHorizontalTextAlign(const TextAlign::H& align);
|
||||
/// Set the vertical alignment of this text widget in relation to it's parent,
|
||||
/// while keeping the text retained inside the parent widget's bounds.
|
||||
/// @see TextAlign::V
|
||||
void SetVerticalTextAlign(const TextAlign::V& align);
|
||||
/// Centers the text of this widget in relation to the parent's bounding box.
|
||||
/// @see SetHorizontalTextAlign, SetVerticalTextAlign, TextAlign enums
|
||||
void AlignCenterBoth();
|
||||
|
||||
/// 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);
|
||||
/// Alias for AlignCenterBoth().
|
||||
void Center();
|
||||
|
||||
/// Aligns the text of this widget to the left-hand-side of the parent's bounding box.
|
||||
/// @see SetHorizontalTextAlign, TextAlign::H
|
||||
void AlignLeft();
|
||||
/// Aligns the text of this widget to the right-hand-side of the parent's bounding box.
|
||||
/// @see SetHorizontalTextAlign, TextAlign::H
|
||||
void AlignRight();
|
||||
/// Aligns the text of this widget to the top of the parent's bounding box.
|
||||
/// @see SetVerticalTextAlign, TextAlign::V
|
||||
void AlignTop();
|
||||
/// Aligns the text of this widget to the bottom of the parent's bounding box.
|
||||
/// @see SetVerticalTextAlign, TextAlign::V
|
||||
void AlignBottom();
|
||||
/// Centers the text of this widget in relation to the parent's bounding box.
|
||||
/// @see SetHorizontalTextAlign, SetVerticalTextAlign, TextAlign enums
|
||||
void AlignCenterBoth();
|
||||
/// Alias for AlignCenterBoth().
|
||||
void Center();
|
||||
/// Aligns the text of this widget to the horizontal center of the parent's bounding box.
|
||||
void AlignCenterHorizontally();
|
||||
/// Aligns the text of this widget to the vertical center of the parent's bounding box.
|
||||
void AlignCenterVertically();
|
||||
/// Aligns the text of this widget to the horizontal center of the parent's bounding box.
|
||||
void AlignCenterHorizontally();
|
||||
|
||||
void Update(float delta);;
|
||||
|
||||
/// 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);
|
||||
|
||||
void SetWordWrap(bool wrap);
|
||||
|
||||
protected:
|
||||
std::string content = "Sample Text";
|
||||
Color4 text_color = {255,255,255,255};
|
||||
float text_outline;
|
||||
Color4 outline_color = {255,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;
|
||||
u32 text_size;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
/// 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})) {};
|
||||
};
|
@@ -19,19 +19,14 @@
|
||||
#include <Color3.hpp>
|
||||
#include <Color4.hpp>
|
||||
#include <JGL/JGL.h>
|
||||
#include <ReWindow/types/Key.h>
|
||||
#include <JUI/Tween.hpp>
|
||||
#include <JUI/JUI.hpp>
|
||||
#include "JUI/DefaultStyle.hpp"
|
||||
|
||||
using namespace JGL;
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
/// An enumeration for mouse buttons, used by JUI to decouple from external systems.
|
||||
/// Some boilerplate is required in order to get input mechanisms up and running. See the demo files for reference.
|
||||
enum class MouseButton {
|
||||
Left = 1,
|
||||
Middle = 2,
|
||||
Right = 3
|
||||
};
|
||||
namespace JUI {
|
||||
|
||||
using namespace J3ML::Math;
|
||||
using namespace J3ML::LinearAlgebra;
|
||||
@@ -39,8 +34,7 @@ namespace JUI
|
||||
/// 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.
|
||||
/// Widgets exist in a tree hierarchy where each object has one parent, and an arbitrary number of children. No circular references are allowed.
|
||||
class Widget
|
||||
{
|
||||
class Widget {
|
||||
public:
|
||||
/// The default constructor for Widget. Generally, JUI widgets will initialize member data,
|
||||
/// and apply a default style in this constructor. Be aware of this, as this is not "idiomatic C++".
|
||||
@@ -49,15 +43,16 @@ namespace JUI
|
||||
Widget(Widget* parent);
|
||||
virtual ~Widget() {}
|
||||
public:
|
||||
#pragma region Events
|
||||
/// An event that triggers when the widget is in-focus, generally when the mouse is hovering it.
|
||||
/// (The actual trigger may differ for each widget)
|
||||
Event<> Focused;
|
||||
/// An event that triggers when the widget loses focus.
|
||||
Event<> Unfocused;
|
||||
/// This event triggers when a new widget is added to this widget, or any descendant of this widget.
|
||||
Event<Widget *> DescendantAdded;
|
||||
Event<Widget *> DescendantAdded; // TODO: Debug
|
||||
/// This event triggers when a widget is removed from this widget's children, or any of it's childrens' children, and so forth.
|
||||
Event<Widget *> DescendantRemoved;
|
||||
Event<Widget *> DescendantRemoved; // TODO: Debug
|
||||
/// This event triggers when the hierarchy this widget is contained within changes.
|
||||
Event<Widget *, Widget*> AncestryChanged;
|
||||
/// This event triggers when a widget is added to this widget's list of children.
|
||||
@@ -66,9 +61,9 @@ namespace JUI
|
||||
Event<Widget *> ChildRemoved;
|
||||
/// This event triggers right before this widget gets deallocated.
|
||||
Event<Widget *> Destroying;
|
||||
#pragma endregion
|
||||
public:
|
||||
|
||||
|
||||
#pragma region Hierarchy
|
||||
/// Adds a given widget to this widget's list of children.
|
||||
/// @return The widget in question.
|
||||
Widget* Add(Widget* newChild);
|
||||
@@ -99,6 +94,24 @@ namespace JUI
|
||||
/// @see GetFamilyTreeRoot().
|
||||
Widget* GetParent() const;
|
||||
|
||||
|
||||
/// Sets the parent object of this widget. Positioning and sizing of a widget is relative to it's parent.
|
||||
void Parent(Widget*);
|
||||
|
||||
/// Returns true if this widget is a 'descendant' of the specified potential ancestor. Otherwise returns false.
|
||||
bool IsDescendantOf(Widget *ancestor);
|
||||
|
||||
/// Returns true if this widget is a 'ancestor' of the specified potential descendant. Otherwise returns false.
|
||||
bool IsAncestorOf(Widget *descendant);
|
||||
|
||||
/// Returns the first ancestor in this widgets hierarchy that does not have its own parent.
|
||||
/// In a well-formed JUI menu, this **should** always be a Scene.
|
||||
Widget* GetFamilyTreeRoot() const;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Layout
|
||||
|
||||
/// Returns the menu-coordinates that are used to position this widget in relation to its parent.
|
||||
/// @see class UDim2, Position(const UDim2&),
|
||||
[[nodiscard]] UDim2 Position() const;
|
||||
@@ -129,18 +142,24 @@ namespace JUI
|
||||
/// @see Size(), class UDim2.
|
||||
void Size(const UDim2&);
|
||||
|
||||
/// Sets the parent object of this widget. Positioning and sizing of a widget is relative to it's parent.
|
||||
void Parent(Widget*);
|
||||
/// Creates and runs an animation on the Position of this widget.
|
||||
Tween* TweenPosition(const UDim2& goal, TweenInfo params = {});
|
||||
|
||||
/// Returns true if this widget is a 'descendant' of the specified potential ancestor. Otherwise returns false.
|
||||
bool IsDescendantOf(Widget *ancestor);
|
||||
|
||||
/// Returns true if this widget is a 'ancestor' of the specified potential descendant. Otherwise returns false.
|
||||
bool IsAncestorOf(Widget *descendant);
|
||||
/// Creates and runs an animation on the Size of this widget.
|
||||
Tween* TweenSize(const UDim2& goal, TweenInfo params = {});
|
||||
|
||||
/// Determines the origin point of a Widget, relative to it's absolute size.
|
||||
[[nodiscard]] Vector2 AnchorPoint() const;
|
||||
|
||||
///
|
||||
void AnchorPoint(const Vector2 &point);
|
||||
|
||||
///
|
||||
Tween* TweenAnchorPoint(const Vector2& goal, TweenInfo info = {});
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Padding
|
||||
/// Returns the padding factor on the left of this widget.
|
||||
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
|
||||
/// @see PaddingLeft(), class UDim.
|
||||
@@ -174,6 +193,7 @@ namespace JUI
|
||||
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
|
||||
/// @see PaddingLeft(), class UDim.
|
||||
void PaddingBottom(const UDim &pad_bottom);
|
||||
|
||||
/// Sets the padding factor on the four respective sides of this widget.
|
||||
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
|
||||
/// @param left
|
||||
@@ -186,6 +206,17 @@ namespace JUI
|
||||
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
|
||||
void Padding(const UDim& padding);
|
||||
|
||||
|
||||
Tween* TweenPaddingLeft(const UDim &goal, TweenInfo info = {});
|
||||
Tween* TweenPaddingRight(const UDim &goal, TweenInfo info = {});
|
||||
Tween* TweenPaddingTop(const UDim &goal, TweenInfo info = {});
|
||||
Tween* TweenPaddingBottom(const UDim &goal, TweenInfo info = {});
|
||||
Tween* TweenPadding(const UDim& goalLeft, const UDim& goalTop, const UDim& goalRight, const UDim& goalBottom, TweenInfo info = {});
|
||||
Tween* TweenPadding(const UDim& goal, TweenInfo info = {});
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Margin
|
||||
/// Returns the margin factor on the left of this widget.
|
||||
[[nodiscard]] UDim MarginLeft() const;
|
||||
/// Returns the margin factor on the top of this widget.
|
||||
@@ -219,6 +250,17 @@ namespace JUI
|
||||
/// @see Margin(const UDim&, const UDim&, const UDim&, const UDim&).
|
||||
void Margin(const UDim& margin);
|
||||
|
||||
Tween* TweenMarginLeft(const UDim &goal, TweenInfo info = {});
|
||||
Tween* TweenMarginTop(const UDim &goal, TweenInfo info = {});
|
||||
Tween* TweenMarginBottom(const UDim &goal, TweenInfo info = {});
|
||||
Tween* TweenMarginRight(const UDim &goal, TweenInfo info = {});
|
||||
|
||||
Tween* TweenMargin(const UDim& goalLeft, const UDim& goalTop, const UDim& goalRight, const UDim& goalBottom, TweenInfo info = {});
|
||||
Tween* TweenMargin(const UDim& goal, TweenInfo info = {});
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Metadata
|
||||
/// Returns this widgets mnemonic name.
|
||||
/// Widgets can optionally be assigned a name that can be used to retrieve it from a widget tree node.
|
||||
/// @see Name().
|
||||
@@ -238,70 +280,114 @@ namespace JUI
|
||||
/// @see IsVisible().
|
||||
void Visible(bool enabled);
|
||||
|
||||
/// Returns the first ancestor in this widgets hierarchy that does not have its own parent.
|
||||
/// In a well-formed JUI menu, this **should** always be a Scene.
|
||||
Widget* GetFamilyTreeRoot() const;
|
||||
|
||||
|
||||
/// Returns whether or not the mouse is inside this widget's approximate bounding-box.
|
||||
bool IsMouseInside() const;
|
||||
|
||||
void AnchorPoint(const Vector2 &point);
|
||||
int LayoutOrder() const { return layout_order; }
|
||||
void LayoutOrder(int value) { layout_order = value;}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
/// Returns the complete bounding box around this instance that will be rendered onto.
|
||||
/// This differs from AbsolutePosition and AbsoluteSize in that they are computed for positioning elements relative to each other.
|
||||
/// Only this method represents the full bounding box of the widget.
|
||||
[[nodiscard]] virtual AABB2D GetActualRenderBounds() const;
|
||||
|
||||
AABB2D AbsoluteBounds() const;
|
||||
|
||||
void SetViewportSize(const Vector2& vps);
|
||||
|
||||
float ComputeElementPadding(float size, const UDim& padding);
|
||||
|
||||
Vector2 ComputeElementPadding(const Vector2& size, const UDim2& padding) const;
|
||||
|
||||
public:
|
||||
|
||||
virtual void PreDraw() {}
|
||||
virtual void PostDraw() {}
|
||||
virtual void InnerDraw() {}
|
||||
|
||||
|
||||
/// Renders the widget to the current OpenGL Context using JGL.
|
||||
/// The user should call this on their Scene instances only. JUI will handle the rest.
|
||||
virtual void Draw();
|
||||
/// Performs the update logic of the widget.
|
||||
/// The user should call this on their Scene instances only. JUI will handle the rest.
|
||||
virtual void Update(float delta);
|
||||
|
||||
/// Informs a widget that the mouse has been moved to a new location.
|
||||
/// This is designed in such a way that the end-user can plug this into their existing code.
|
||||
/// The user should call this on their Scene instances only. JUI will handle the rest.
|
||||
/// See ReWindowIntegrationDemo for an example.
|
||||
virtual void ObserveMouseMovement(const Vector2& latest_known_pos);
|
||||
/// @return True if this widget, or one of its descendants should "consume" the input event,
|
||||
/// meaning it no longer needs to be passed to the next widgets in the hierarchy.
|
||||
/// @note It is acceptable for a widget to "observe" the input event, but not "consume" it.
|
||||
virtual bool ObserveMouseMovement(const Vector2& latest_known_pos);
|
||||
|
||||
/// Informs a widget that a mouse button has pressed or released.
|
||||
/// This is designed in such a way that the end-user can plug this into their existing code.
|
||||
/// The user should call this on their Scene instances only. JUI will handle the rest.
|
||||
/// See ReWindowIntegrationDemo for an example.
|
||||
virtual void ObserveMouseInput(MouseButton btn, bool pressed);
|
||||
/// @return True if this widget, or one of its descendants should "consume" the input event,
|
||||
/// meaning it no longer needs to be passed to the next widgets in the hierarchy.
|
||||
/// @note It is acceptable for a widget to "observe" the input event, but not "consume" it.
|
||||
virtual bool ObserveMouseInput(MouseButton btn, bool pressed);
|
||||
|
||||
/// Informs a widget that the mouse wheel has been moved.
|
||||
/// This is designed in such a way that the end-user can plug this into their existing code.
|
||||
/// The user should call this on their Scene instances only. JUI will handle the rest.
|
||||
/// See ReWindowIntegrationDemo for an example.
|
||||
virtual bool ObserveMouseWheel(int mwheel);
|
||||
|
||||
/// Informs a widget that a key has been pressed or released.
|
||||
/// This is designed in such a way that the end-user can plug this into their existing code.
|
||||
/// The user should call this on their Scene instances only. JUI will handle the rest.
|
||||
/// See ReWindowIntegrationDemo for an example.
|
||||
virtual bool ObserveKeyInput(Key key, bool pressed);
|
||||
|
||||
protected:
|
||||
void DrawChildWidgets();
|
||||
void UpdateChildWidgets(float delta);
|
||||
protected:
|
||||
MouseButton mbtn;
|
||||
bool mb_state;
|
||||
bool prev_mb_state;
|
||||
//int last_known_mouse_button;
|
||||
//bool last_known_mouse_button_state;
|
||||
Vector2 last_known_mouse_pos;
|
||||
UDim2 position;
|
||||
UDim2 size;
|
||||
MouseButton mbtn = MouseButton::Left;
|
||||
bool mb_state = false;
|
||||
bool prev_mb_state = false;
|
||||
Vector2 last_known_mouse_pos = {0,0};
|
||||
UDim2 position = {0_px, 0_px};
|
||||
UDim2 size = {50_px, 50_px};
|
||||
Widget* parent = nullptr;
|
||||
std::vector<Widget*> children;
|
||||
std::vector<Tween*> tweens;
|
||||
float rotation = 0;
|
||||
std::string name;
|
||||
bool selected = false;
|
||||
UDim pad_left = 0_px;
|
||||
UDim pad_right = 0_px;
|
||||
UDim pad_top = 0_px;
|
||||
UDim pad_bottom = 0_px;
|
||||
UDim margin_left = 0_px;
|
||||
UDim margin_right = 0_px;
|
||||
UDim margin_top = 0_px;
|
||||
UDim margin_bottom = 0_px;
|
||||
UDim pad_left = Style::BasePadding;
|
||||
UDim pad_right = Style::BasePadding;
|
||||
UDim pad_top = Style::BasePadding;
|
||||
UDim pad_bottom = Style::BasePadding;
|
||||
UDim margin_left = Style::BasePadding;
|
||||
UDim margin_right = Style::BasePadding;
|
||||
UDim margin_top = Style::BasePadding;
|
||||
UDim margin_bottom = Style::BasePadding;
|
||||
Vector2 anchor_point = {0.f, 0.f};
|
||||
bool visible = true;
|
||||
Widget* next = nullptr;
|
||||
Widget* prev = nullptr;
|
||||
int zindex;
|
||||
int zindex = 0;
|
||||
int layout_order = 0;
|
||||
Vector2 viewport_size{0,0};
|
||||
|
||||
/// Returns the amount of pixels this widget will be padded by from the top-left.
|
||||
/// Generally, the widget will be shrunk and moved over by this amount, relative to the parent.
|
||||
Vector2 GetAbsolutePaddingTopLeft() const;
|
||||
|
||||
/// Returns the amount of pixels this widget will be padded by from bottom-right.
|
||||
/// Generally, the widget will be shrunk by twice this amount, relative to the parent.
|
||||
Vector2 GetAbsolutePaddingBottomRight() const;
|
||||
Vector2 GetAbsoluteMarginTopLeft();
|
||||
Vector2 GetAbsoluteMarginBottomRight();
|
||||
void UpdateTweens(float elapsed);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
72
include/JUI/DefaultStyle.hpp
Normal file
72
include/JUI/DefaultStyle.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <Color4.hpp>
|
||||
#include <Colors.hpp>
|
||||
#include <JUI/UDim.hpp>
|
||||
#include <JUI/UDim2.hpp>
|
||||
|
||||
namespace JUI {
|
||||
|
||||
namespace DefaultStyle {
|
||||
const Color4 BackgroundColor = {128, 128, 128};
|
||||
const Color4 BorderColor = {192, 192, 192};
|
||||
const float BorderLineWidth = 1.f;
|
||||
const UDim BasePadding = 0_px;
|
||||
|
||||
namespace Button {
|
||||
const Color4 BaseBackgroundColor = Colors::White;
|
||||
const Color4 BaseBorderColor = Colors::Black;
|
||||
const Color4 HoverBackgroundColor = Colors::Blues::SkyBlue;
|
||||
const Color4 HoverBorderColor = Colors::Blues::DarkSlateBlue;
|
||||
const Color4 PressBackgroundColor = Colors::Blues::DarkSlateBlue;
|
||||
const Color4 PressBorderColor = Colors::Blues::SkyBlue;
|
||||
const Color4 DisableBackgroundColor = Colors::Gray;
|
||||
const Color4 DisableBorderColor = Colors::Gray;
|
||||
}
|
||||
|
||||
namespace Image
|
||||
{
|
||||
//const
|
||||
}
|
||||
|
||||
namespace Text {
|
||||
namespace H1 {
|
||||
|
||||
}
|
||||
namespace H2 {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace InputForm {
|
||||
const Color4 AutocompleteTextColor = Colors::Black;
|
||||
}
|
||||
|
||||
namespace Collapsible {
|
||||
const UDim HeaderHeight = 16_px;
|
||||
}
|
||||
|
||||
namespace Checkbox {
|
||||
const Color4 CheckmarkColor = Colors::Red;
|
||||
|
||||
}
|
||||
namespace Window {
|
||||
const UDim2 DefaultSize = {200_px, 200_px};
|
||||
const Vector2 DefaultMinimumSize = {200, 200};
|
||||
const int TitlebarHeight = 20;
|
||||
const Color4 OutlineColor = {92, 92, 192};
|
||||
const Color4 UnfocusedOutlineColor = Colors::Gray;
|
||||
const Color4 ViewportBackgroundColor = Colors::DarkGray;
|
||||
const int OutlineWidth = 2;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ExperimentalStyle {
|
||||
|
||||
}
|
||||
|
||||
namespace Style = DefaultStyle;
|
||||
}
|
@@ -1,8 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <jlog/Logger.hpp>
|
||||
#include <Event.h>
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
extern jlog::GenericLogger UILogs;
|
||||
namespace JUI {
|
||||
class JUILogger : public jlog::GenericLogger {
|
||||
public:
|
||||
Event<std::string, Color4> OnLog;
|
||||
|
||||
JUILogger(const std::string& context,
|
||||
std::ofstream& file,
|
||||
Color4 contextColor = Colors::White,
|
||||
Color4 timestampColor = Colors::Purples::Fuchsia,
|
||||
Color4 locationColor = Colors::Pinks::Pink,
|
||||
Color4 pointerColor = Colors::Pinks::LightPink,
|
||||
Color4 messageColor = Colors::Greens::LightGreen);
|
||||
|
||||
void Log(const std::string& message, const std::source_location &location = std::source_location::current(), const jlog::Timestamp &ts = jlog::Timestamp()) override;
|
||||
};
|
||||
|
||||
extern JUILogger UILogs;
|
||||
|
||||
/// 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
|
||||
};
|
||||
}
|
@@ -20,10 +20,11 @@ namespace JUI
|
||||
class Clickable
|
||||
{
|
||||
public:
|
||||
|
||||
Event<Vector2, MouseButton> OnClickEvent;
|
||||
Event<Vector2, MouseButton, bool> OnReleaseEvent;
|
||||
public:
|
||||
bool IsClicked() const { return clicked; };
|
||||
[[nodiscard]] bool IsClicked() const { return clicked; };
|
||||
void SetClicked(bool manual_click);
|
||||
public:
|
||||
virtual void OnClick(const Vector2& MousePos, const MouseButton& MouseButton);;
|
||||
|
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
|
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <J3ML/LinearAlgebra/Vector2.hpp>
|
||||
#include <Event.h>
|
||||
#include <JUI/Widgets/Tooltip.hpp>
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
@@ -26,12 +27,16 @@ namespace JUI
|
||||
public:
|
||||
bool IsHovered() const { return hovered; };
|
||||
public:
|
||||
virtual void OnHover(const Vector2& MousePos);;
|
||||
virtual void OnExit(const Vector2& MousePos);;
|
||||
virtual void OnHover(const Vector2& MousePos);
|
||||
virtual void OnExit(const Vector2& MousePos);
|
||||
|
||||
void Update(const Vector2& m_pos, float delta);
|
||||
//virtual void SetTooltip(const std::string& content, float delay) {}
|
||||
protected:
|
||||
bool hovered;
|
||||
bool hover_debounce;
|
||||
//Tooltip* tooltip = nullptr;
|
||||
//float tooltip_threshold = 0.f;
|
||||
//float tooltip_limit = 0.125f;
|
||||
bool hovered = false;
|
||||
bool hover_debounce = false;
|
||||
};
|
||||
}
|
@@ -1,3 +1,14 @@
|
||||
/// 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 Toggleable.hpp
|
||||
/// @desc Toggleable Mixin Helper Class - Added to widgets that should have special behavior when hovered by the mouse.
|
||||
/// @edit 2024-10-11
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Event.h>
|
||||
|
137
include/JUI/Tween.hpp
Normal file
137
include/JUI/Tween.hpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "Event.h"
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
using EasingFunc = std::function<float(float)>;
|
||||
using TweenTickFunc = std::function<void(float, float)>;
|
||||
|
||||
namespace EasingFunctions
|
||||
{
|
||||
|
||||
float EaseInOutLinear(float t);
|
||||
|
||||
/// Speed is determined by a sine wave for gentle easing motion.
|
||||
float EaseInSine(float t);
|
||||
/// Speed is determined by a sine wave for gentle easing motion.
|
||||
float EaseOutSine(float t);
|
||||
/// Speed is determined by a sine wave for gentle easing motion.
|
||||
float EaseInOutSine(float t);
|
||||
|
||||
/// Similar to Sine but with a slightly sharper curve based on quadratic interpolation.
|
||||
float EaseInQuad(float t);
|
||||
/// Similar to Sine but with a slightly sharper curve based on quadratic interpolation.
|
||||
float EaseOutQuad(float t);
|
||||
/// Similar to Sine but with a slightly sharper curve based on quadratic interpolation.
|
||||
float EaseInOutQuad(float t);
|
||||
|
||||
/// Similar to Quad but with a slightly sharper curve based on cubic interpolation.
|
||||
float EaseInCubic(float t);
|
||||
/// Similar to Quad but with a slightly sharper curve based on cubic interpolation.
|
||||
float EaseOutCubic(float t);
|
||||
/// Similar to Quad but with a slightly sharper curve based on cubic interpolation.
|
||||
float EaseInOutCubic(float t);
|
||||
|
||||
/// Similar to Cubic but with an even sharper curve based on quartic interpolation.
|
||||
float EaseInQuart(float t);
|
||||
/// Similar to Cubic but with an even sharper curve based on quartic interpolation.
|
||||
float EaseOutQuart(float t);
|
||||
/// Similar to Cubic but with an even sharper curve based on quartic interpolation.
|
||||
float EaseInOutQuart(float t);
|
||||
|
||||
/// Similar to Quart but with an even sharper curve based on quintic interpolation.
|
||||
float EaseInQuint(float t);
|
||||
/// Similar to Quart but with an even sharper curve based on quintic interpolation.
|
||||
float EaseOutQuint(float t);
|
||||
/// Similar to Quart but with an even sharper curve based on quintic interpolation.
|
||||
float EaseInOutQuint(float t);
|
||||
|
||||
/// The sharpest curve based on exponential interpolation.
|
||||
float EaseInExpo(float t);
|
||||
/// The sharpest curve based on exponential interpolation.
|
||||
float EaseOutExpo(float t);
|
||||
/// The sharpest curve based on exponential interpolation.
|
||||
float EaseInOutExpo(float t);
|
||||
|
||||
/// Follows a circular arc, such that acceleration is more sudden and deceleration more gradual versus Quint or Exponential.
|
||||
float EaseInCirc(float t);
|
||||
/// Follows a circular arc, such that acceleration is more sudden and deceleration more gradual versus Quint or Exponential.
|
||||
float EaseOutCirc(float t);
|
||||
/// Follows a circular arc, such that acceleration is more sudden and deceleration more gradual versus Quint or Exponential.
|
||||
float EaseInOutCirc(float t);
|
||||
|
||||
/// Slightly overshoots the target, then backs into place.
|
||||
float EaseInBack(float t);
|
||||
/// Slightly overshoots the target, then backs into place.
|
||||
float EaseOutBack(float t);
|
||||
/// Slightly overshoots the target, then backs into place.
|
||||
float EaseInOutBack(float t);
|
||||
|
||||
float EaseInElastic(float t);
|
||||
float EaseOutElastic(float t);
|
||||
float EaseInOutElastic(float t);
|
||||
|
||||
float EaseInBounce(float t);
|
||||
float EaseOutBounce(float t);
|
||||
float EaseInOutBounce(float t);
|
||||
}
|
||||
|
||||
struct TweenInfo {
|
||||
float time = 1.f;
|
||||
float delay = 0.f;
|
||||
int repeats = 0;
|
||||
bool reverse = false;
|
||||
EasingFunc easing = &EasingFunctions::EaseInOutLinear;
|
||||
bool overwritable = true;
|
||||
};
|
||||
|
||||
/// A class that represents an animation-in-action.
|
||||
class Tween {
|
||||
public:
|
||||
Tween(TweenTickFunc tick_func);
|
||||
Tween(TweenTickFunc tick_func, const TweenInfo& info) {
|
||||
this->tick_func = tick_func;
|
||||
|
||||
this->time = info.time;
|
||||
this->delay_time = info.delay;
|
||||
this->repeat_count = info.repeats;
|
||||
this->reverses = info.reverse;
|
||||
this->easing_func = info.easing;
|
||||
this->overwritable = info.overwritable;
|
||||
}
|
||||
Event<> Completed;
|
||||
|
||||
void Update(float elapsed);
|
||||
|
||||
std::function<float(float)> easing_func = &EasingFunctions::EaseInOutLinear;
|
||||
std::function<void(float, float)> tick_func;
|
||||
|
||||
/// Duration of the tween, in seconds.
|
||||
float time = 5;
|
||||
/// Time of delay until the tween begins, in seconds.
|
||||
float delay_time = 0;
|
||||
/// Number of times the tween repeats. -1 indicates indefinite repetition.
|
||||
int repeat_count = 0; // TODO: Implement
|
||||
/// Whether or not the tween interpolates in reverse once the initial tween completes.
|
||||
bool reverses = false; // TODO: Implement
|
||||
|
||||
float progress = 0;
|
||||
bool alive = true;
|
||||
bool paused = false;
|
||||
bool completed = false;
|
||||
bool overwritable = true;
|
||||
|
||||
|
||||
void Cancel();
|
||||
void ForceFinish();
|
||||
void Pause() { paused = true; }
|
||||
void Resume() { paused = false; }
|
||||
void Start();
|
||||
|
||||
bool Paused() const;
|
||||
bool HasCompleted() const;
|
||||
};
|
||||
}
|
@@ -11,6 +11,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "J3ML/J3ML.hpp"
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
/// A coordinate system data type for user interfaces specifically.
|
||||
@@ -32,6 +34,10 @@ namespace JUI
|
||||
UDim operator * (float rhs) const;
|
||||
|
||||
UDim operator / (float rhs) const;
|
||||
|
||||
bool Equals(const UDim& rhs, float epsilon = 1e-3f);
|
||||
|
||||
UDim Lerp(const UDim& goal, float t);
|
||||
};
|
||||
|
||||
namespace UDimLiterals {
|
||||
|
@@ -42,6 +42,10 @@ namespace JUI
|
||||
|
||||
static UDim2 FromScale(float x, float y);
|
||||
|
||||
UDim2 Lerp(const UDim2& goal, float t);
|
||||
|
||||
bool Equals(const UDim2& rhs, float epsilon = 1e-3f);
|
||||
|
||||
public: // Operators
|
||||
UDim2 operator +(const UDim2& rhs) const;
|
||||
|
||||
|
25
include/JUI/Widgets/BindMenu.h
Normal file
25
include/JUI/Widgets/BindMenu.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Widgets/Window.hpp>
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
|
||||
struct BindAction
|
||||
{
|
||||
std::string id;
|
||||
std::string display_name;
|
||||
std::vector<std::string> key_ids;
|
||||
};
|
||||
|
||||
class BindMenu : public Window {
|
||||
public:
|
||||
explicit BindMenu();
|
||||
|
||||
void AddAction(std::string id, std::string disp_name);
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
@@ -29,7 +29,7 @@ namespace JUI
|
||||
|
||||
/// The Button class is a basic rect widget that accepts mouse inputs, and provides event hooks.
|
||||
/// The button also provides built-in automatic coloring based on it's current state, which are listed above.
|
||||
class Button : public Widget, public RectBase, public Clickable, public Hoverable
|
||||
class Button : public Rect, public Clickable, public Hoverable
|
||||
{
|
||||
public:
|
||||
Event<> OnEnabled;
|
||||
@@ -83,6 +83,10 @@ namespace JUI
|
||||
/// Sets the border color of this object when it is disabled.
|
||||
void DisabledBorderColor(const Color4& color);
|
||||
|
||||
void BGColors(const Color4& base, const Color4& hover, const Color4& pressed, const Color4& disabled = Colors::Gray);
|
||||
|
||||
void BorderColors(const Color4& base, const Color4& hover, const Color4& pressed, const Color4& disabled = Colors::Gray);
|
||||
|
||||
/// Returns whether is button is interactable. If enabled, it will listen to mouse events and react accordingly.
|
||||
bool Enabled() const;
|
||||
/// Returns whether is button is interactable. If enabled, it will listen to mouse events and react accordingly.
|
||||
@@ -95,23 +99,26 @@ namespace JUI
|
||||
/// Enables this button. @see Disable, SetEnabled
|
||||
void Enable();
|
||||
|
||||
void Draw() override;
|
||||
void Update(float delta) override;
|
||||
|
||||
void OnClick(const Vector2& mouse_pos, const MouseButton& btn) override;
|
||||
void OnRelease(const Vector2& mouse_pos, const MouseButton& bnt, bool still_hovering) override;
|
||||
void OnHover(const J3ML::LinearAlgebra::Vector2 &MousePos) override;
|
||||
void OnExit(const J3ML::LinearAlgebra::Vector2 &MousePos) override;
|
||||
//void SetTooltip(const std::string &content, float delay = 0.2f) override;
|
||||
protected:
|
||||
bool disabled = false;
|
||||
Color4 hover_bg;
|
||||
Color4 hover_border;
|
||||
Color4 pressed_bg;
|
||||
Color4 pressed_border;
|
||||
Color4 disabled_bg;
|
||||
Color4 disabled_border;
|
||||
Color4 base_bg;
|
||||
Color4 base_border;
|
||||
|
||||
Color4 hover_bg = Style::Button::HoverBackgroundColor;
|
||||
Color4 hover_border = Style::Button::HoverBorderColor;
|
||||
Color4 pressed_bg = Style::Button::PressBackgroundColor;
|
||||
Color4 pressed_border = Style::Button::PressBorderColor;
|
||||
Color4 disabled_bg = Style::Button::DisableBackgroundColor;
|
||||
Color4 disabled_border = Style::Button::DisableBorderColor;
|
||||
Color4 base_bg = Style::Button::BaseBackgroundColor;
|
||||
Color4 base_border = Style::Button::BaseBorderColor;
|
||||
|
||||
void UpdateVisualState();
|
||||
};
|
||||
|
||||
|
||||
|
@@ -1,8 +0,0 @@
|
||||
//
|
||||
// Created by dawsh on 8/1/24.
|
||||
//
|
||||
|
||||
#ifndef JUI_CANVAS_HPP
|
||||
#define JUI_CANVAS_HPP
|
||||
|
||||
#endif //JUI_CANVAS_HPP
|
@@ -13,49 +13,43 @@
|
||||
|
||||
#include <JUI/Base/Widget.hpp>
|
||||
#include <JUI/Widgets/Rect.hpp>
|
||||
#include "JUI/Mixins/Clickable.hpp"
|
||||
#include "JUI/Mixins/Hoverable.hpp"
|
||||
#include "Button.hpp"
|
||||
#include "JUI/Base/ImageBase.hpp"
|
||||
#include <JUI/Mixins/Clickable.hpp>
|
||||
#include <JUI/Mixins/Hoverable.hpp>
|
||||
#include <JUI/Widgets/Button.hpp>
|
||||
#include <JUI/Base/ImageBase.hpp>
|
||||
#include <JUI/Widgets/ImageButton.hpp>
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
class Checkbox : public Button
|
||||
{
|
||||
namespace JUI {
|
||||
// TODO: Find a nice way to implement a checkmark.
|
||||
|
||||
class CheckboxBase {
|
||||
public:
|
||||
[[nodiscard]] Color4 CheckedColor() const { return check_color; }
|
||||
void CheckedColor(const Color4& color) { check_color = color; }
|
||||
bool IsChecked() const { return checked;}
|
||||
void SetChecked(bool value) { checked = value; }
|
||||
Tween* TweenCheckedColor(const Color4& goal, TweenInfo info = {});
|
||||
protected:
|
||||
bool checked = false;
|
||||
Color4 check_color = Style::Checkbox::CheckmarkColor;
|
||||
};
|
||||
|
||||
class Checkbox : public Button, public CheckboxBase {
|
||||
public:
|
||||
Checkbox();
|
||||
explicit Checkbox(Widget* parent);
|
||||
|
||||
void Update(float delta) override
|
||||
{
|
||||
Button::Update(delta);
|
||||
}
|
||||
|
||||
|
||||
void OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &bnt, bool still_hovering) override
|
||||
{
|
||||
Button::OnRelease(mouse_pos, bnt, still_hovering);
|
||||
|
||||
}
|
||||
|
||||
void Draw() override
|
||||
{
|
||||
Button::Draw();
|
||||
|
||||
if (checked)
|
||||
{
|
||||
J2D::Begin();
|
||||
//J2D::DrawLine(check_color, );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
explicit Checkbox(Widget *parent);
|
||||
void Update(float delta) override;
|
||||
void OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &bnt, bool still_hovering) override;
|
||||
void InnerDraw() override;
|
||||
|
||||
protected:
|
||||
bool checked;
|
||||
Color4 check_color;
|
||||
private:
|
||||
};
|
||||
|
||||
class ImageCheckbox : public ImageButton, public CheckboxBase {
|
||||
public:
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
27
include/JUI/Widgets/CheckboxLabel.hpp
Normal file
27
include/JUI/Widgets/CheckboxLabel.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();
|
||||
};
|
||||
}
|
45
include/JUI/Widgets/Collapsible.hpp
Normal file
45
include/JUI/Widgets/Collapsible.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
/// Josh's User Interface Library
|
||||
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
|
||||
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
|
||||
/// Special Thanks to William Tomasine II and Maxine Hayes.
|
||||
/// (c) 2025 Redacted Software
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file Collapsible.cpp
|
||||
/// @desc A rectangular widget with a clickable header that enables hiding and showing the contents.
|
||||
/// @edit 2025-02-14
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Widgets/Rect.hpp>
|
||||
#include <JUI/Widgets/TextButton.hpp>
|
||||
|
||||
namespace JUI {
|
||||
class Collapsible : public Rect {
|
||||
public:
|
||||
Collapsible();
|
||||
explicit Collapsible(Widget* parent);
|
||||
TextButton* Header();
|
||||
Rect* ContentBox();
|
||||
void Collapse();
|
||||
void Expand();
|
||||
[[nodiscard]] bool Collapsed() const;
|
||||
|
||||
UDim HeaderHeight() const;
|
||||
void HeaderHeight(const UDim& value);
|
||||
|
||||
void Title(const std::string& value);
|
||||
std::string Title() const;
|
||||
|
||||
|
||||
protected:
|
||||
TextButton* header = nullptr;
|
||||
Rect* content_box = nullptr;
|
||||
TextRect* lil_arrow = nullptr;
|
||||
bool collapsed = false;
|
||||
UDim2 saved_size;
|
||||
UDim header_height = Style::Collapsible::HeaderHeight;
|
||||
Tween* resize_anim = nullptr;
|
||||
};
|
||||
|
||||
|
||||
}
|
78
include/JUI/Widgets/CommandLine.hpp
Normal file
78
include/JUI/Widgets/CommandLine.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Widgets/Window.hpp>
|
||||
#include <JUI/Widgets/TextInputForm.hpp>
|
||||
#include <JUI/Widgets/ScrollingRect.hpp>
|
||||
#include <JUI/Widgets/ListLayout.hpp>
|
||||
|
||||
namespace JUI {
|
||||
|
||||
// TODO: Implement "Rich Text" message support.
|
||||
// TODO: Implement command autocomplete helper API.
|
||||
// TODO: Implement API for user-projects to bind commands.
|
||||
// TODO: Implement API for user-projects to bind loggers.
|
||||
// TODO: Implement message history.
|
||||
|
||||
// TODO: As of now, the API user is in charge of parsing and executing commands.
|
||||
// TODO: A fairly-generic CommandInterpreter which suits our purposes.
|
||||
// TODO: <command> <arg1> <arg2> <arg3>
|
||||
// TODO: Tight JSON integration for specifying complex metadata in commands.
|
||||
|
||||
/// A generic "Game Console", which provides a log history, and input box for user-defined commands.
|
||||
class CommandLine : public Window {
|
||||
public:
|
||||
|
||||
/// This event is invoked when the user has sent a message to the command line.
|
||||
Event<std::string> OnInput;
|
||||
/// 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.
|
||||
CommandLine();
|
||||
|
||||
/// Constructs a command line by specifying the widget it should be parented to.
|
||||
/// @param parent
|
||||
explicit CommandLine(Widget* parent);
|
||||
|
||||
/// Adds a message to the CommandLine message history.
|
||||
/// @param message
|
||||
/// @param color
|
||||
void Log(const std::string& message, const Color4& color = Colors::White);
|
||||
|
||||
/// @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);
|
||||
|
||||
/// Passes input events down the widget hierarchy.
|
||||
/// @return True if this widget will "consume" the input.
|
||||
bool ObserveKeyInput(Key key, bool pressed) override;
|
||||
|
||||
/// Invoked when a message is sent to the command line.
|
||||
virtual void OnInputBoxSend(const std::string& message);
|
||||
|
||||
protected:
|
||||
TextInputForm* input_box;
|
||||
ScrollingRect* message_log_box;
|
||||
VerticalListLayout* message_log_list;
|
||||
protected:
|
||||
int index = 0;
|
||||
int input_form_height = 20;
|
||||
int message_height = 16;
|
||||
bool open = false;
|
||||
std::vector<std::string> msg_history;
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
//
|
||||
// Created by dawsh on 8/1/24.
|
||||
//
|
||||
|
||||
#ifndef JUI_CONTEXTMENU_HPP
|
||||
#define JUI_CONTEXTMENU_HPP
|
||||
|
||||
#endif //JUI_CONTEXTMENU_HPP
|
@@ -22,7 +22,7 @@ namespace JUI
|
||||
{
|
||||
public:
|
||||
GridLayout() : LayoutContainer() {
|
||||
|
||||
Name("GridLayout");
|
||||
}
|
||||
explicit GridLayout(Widget* parent) : GridLayout()
|
||||
{
|
||||
|
37
include/JUI/Widgets/ImageButton.hpp
Normal file
37
include/JUI/Widgets/ImageButton.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Base/ImageBase.hpp>
|
||||
#include <JUI/Widgets/Button.hpp>
|
||||
|
||||
namespace JUI {
|
||||
class ImageButton;
|
||||
}
|
||||
|
||||
class JUI::ImageButton : public ImageBase, public Button {
|
||||
public:
|
||||
ImageButton();
|
||||
explicit ImageButton(Widget* parent);
|
||||
~ImageButton() override = default;
|
||||
void Update(float delta) override;
|
||||
void Draw() override;
|
||||
|
||||
[[nodiscard]] Color4 HoveredImageColor() const;
|
||||
[[nodiscard]] Color4 BaseImageColor() const;
|
||||
[[nodiscard]] Color4 PressedImageColor() const;
|
||||
[[nodiscard]] Color4 DisabledImageColor() const;
|
||||
|
||||
void HoveredImageColor(const Color4& value);
|
||||
void BaseImageColor(const Color4& value);
|
||||
void PressedImageColor(const Color4& value);
|
||||
void DisabledImageColor(const Color4& value);
|
||||
|
||||
void ImageColors(const Color4& hover, const Color4& base, const Color4& press, const Color4& disabled);
|
||||
|
||||
protected:
|
||||
void UpdateImageVisualState();
|
||||
protected:
|
||||
Color4 hover_img_color = Colors::White;
|
||||
Color4 base_img_color = Colors::White;
|
||||
Color4 pressed_img_color = Colors::White;
|
||||
Color4 disabled_img_color = Colors::White;
|
||||
};
|
@@ -9,17 +9,23 @@
|
||||
/// @desc A widget that contains and renders an image provided by JGL, within a rectangular frame.
|
||||
/// @edit 2024-08-05
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <JUI/Widgets/Rect.hpp>
|
||||
#include <JUI/Base/ImageBase.hpp>
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
class ImageRect: public Rect, public ImageBase
|
||||
{
|
||||
namespace JUI {
|
||||
class ImageRect: public Rect, public ImageBase {
|
||||
public:
|
||||
ImageRect();
|
||||
explicit ImageRect(Widget* parent);
|
||||
void Update(float delta) override;
|
||||
|
||||
void Draw() override;
|
||||
|
||||
bool FitImageToBounds() const { return fit_image_to_bounds;}
|
||||
void FitImageToBounds(bool value) { fit_image_to_bounds = value;}
|
||||
protected:
|
||||
bool fit_image_to_bounds;
|
||||
};
|
||||
}
|
14
include/JUI/Widgets/ListBox.hpp
Normal file
14
include/JUI/Widgets/ListBox.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <JUI/Widgets/ScrollingRect.hpp>
|
||||
|
||||
namespace JUI {
|
||||
|
||||
class ListBox : public ScrollingRect
|
||||
{
|
||||
ListBox();
|
||||
explicit ListBox(Widget* widget);
|
||||
};
|
||||
|
||||
}
|
@@ -31,30 +31,35 @@ namespace JUI
|
||||
|
||||
/// Lays child elements out in a vertical list.
|
||||
/// Child element positions are overridden by this widget.
|
||||
class VerticalListLayout : public ListLayout
|
||||
{
|
||||
class VerticalListLayout : public ListLayout {
|
||||
public:
|
||||
VerticalListLayout();
|
||||
explicit VerticalListLayout(Widget* parent);
|
||||
|
||||
/// Computes the order and positioning of all child elements to display them in an ordered list.
|
||||
void ApplyLayout() override;
|
||||
void LayoutOrder(LayoutOrder::V order);
|
||||
[[nodiscard]] LayoutOrder::V LayoutOrder() const;
|
||||
|
||||
/// @return the most-recently-computed sum of the heights of all child elements, with padding also taken into account.
|
||||
[[nodiscard]] float CurrentContentHeight() const;
|
||||
protected:
|
||||
LayoutOrder::V layout = LayoutOrder::V::TOP;
|
||||
float content_height = 0;
|
||||
};
|
||||
|
||||
/// Lays child elements out in a horizontal list.
|
||||
/// Child element positions are overridden by this widget.
|
||||
class HorizontalListLayout : public ListLayout
|
||||
{
|
||||
class HorizontalListLayout : public ListLayout {
|
||||
public:
|
||||
HorizontalListLayout();
|
||||
explicit HorizontalListLayout(Widget* parent);
|
||||
void ApplyLayout() override;
|
||||
void LayoutOrder(LayoutOrder::H order);
|
||||
[[nodiscard]] LayoutOrder::H LayoutOrder() const;
|
||||
[[nodiscard]] float CurrentContentWidth() const;
|
||||
protected:
|
||||
LayoutOrder::H layout = LayoutOrder::H::LEFT;
|
||||
float content_width = 0;
|
||||
};
|
||||
}
|
83
include/JUI/Widgets/NineSlice.hpp
Normal file
83
include/JUI/Widgets/NineSlice.hpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/// Josh's User Interface Library
|
||||
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
|
||||
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
|
||||
/// Special Thanks to William Tomasine II and Maxine Hayes.
|
||||
/// (c) 2024 Redacted Software
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file NineSlice.hpp
|
||||
/// @desc A widget that implements 9-slice scaling on an image.
|
||||
/// @edit 2025-2-2
|
||||
|
||||
/// https://en.wikipedia.org/wiki/9-slice_scaling
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Base/Widget.hpp>
|
||||
#include <JUI/Widgets/Rect.hpp>
|
||||
#include <JUI/Base/ImageBase.hpp>
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
class NineSliceRect : public Rect, public ImageBase {
|
||||
|
||||
public:
|
||||
NineSliceRect();
|
||||
explicit NineSliceRect(Widget* parent);
|
||||
NineSliceRect(Widget* parent, JGL::Texture* texture);
|
||||
|
||||
/// Returns the bounds of the 'Top-Left' slice of the 9-slice.
|
||||
[[nodiscard]] AABB2D TopLeftQuad() const;
|
||||
/// Returns the bounds of the 'Top-Right' slice of the 9-slice.
|
||||
[[nodiscard]] AABB2D TopRightQuad() const;
|
||||
/// Returns the bounds of the 'Bottom-Left' slice of the 9-slice.
|
||||
[[nodiscard]] AABB2D BottomLeftQuad() const;
|
||||
/// Returns the bounds of the 'Bottom-Right' slice of the 9-slice.
|
||||
[[nodiscard]] AABB2D BottomRightQuad() const;
|
||||
|
||||
/// Returns the bounds of the 'Top' slice of the 9-slice.
|
||||
[[nodiscard]] AABB2D TopQuad() const;
|
||||
/// Returns the bounds of the 'Left' slice of the 9-slice.
|
||||
[[nodiscard]] AABB2D LeftQuad() const;
|
||||
/// Returns the bounds of the 'Right' slice of the 9-slice.
|
||||
[[nodiscard]] AABB2D RightQuad() const;
|
||||
/// Returns the bounds of the 'Bottom' slice of the 9-slice.
|
||||
[[nodiscard]] AABB2D BottomQuad() const;
|
||||
/// Returns the bounds of the 'Center' slice of the 9-slice.
|
||||
[[nodiscard]] AABB2D CenterQuad() const;
|
||||
|
||||
/// Sets the bounds of the quadrant for the 'Top-Left' slice of the 9-slice.
|
||||
void TopLeftQuad(const AABB2D& quad);
|
||||
/// Sets the bounds of the quadrant for the 'Top-Right' slice of the 9-slice.
|
||||
void TopRightQuad(const AABB2D& quad);
|
||||
/// Sets the bounds of the quadrant for the 'Bottom-Left' slice of the 9-slice.
|
||||
void BottomLeftQuad(const AABB2D& quad);
|
||||
/// Sets the bounds of the quadrant for the 'Bottom-Right' slice of the 9-slice.
|
||||
void BottomRightQuad(const AABB2D& quad);
|
||||
/// Sets the bounds of the quadrant for the 'Top' slice of the 9-slice.
|
||||
void TopQuad(const AABB2D& quad);
|
||||
/// Sets the bounds of the quadrant for the 'Right' slice of the 9-slice.
|
||||
void RightQuad(const AABB2D& quad);
|
||||
/// Sets the bounds of the quadrant for the 'Bottom' slice of the 9-slice.
|
||||
void BottomQuad(const AABB2D& quad);
|
||||
/// Sets the bounds of the quadrant for the 'Left' slice of the 9-slice.
|
||||
void LeftQuad(const AABB2D& quad);
|
||||
/// Sets the bounds of the quadrant for the 'Center' slice of the 9-slice.
|
||||
void CenterQuad(const AABB2D& quad);
|
||||
|
||||
void Draw() override;
|
||||
protected:
|
||||
AABB2D top_left_quad;
|
||||
AABB2D top_right_quad;
|
||||
AABB2D bottom_left_quad;
|
||||
AABB2D bottom_right_quad;
|
||||
|
||||
AABB2D top_quad;
|
||||
AABB2D bottom_quad;
|
||||
AABB2D right_quad;
|
||||
AABB2D left_quad;
|
||||
|
||||
AABB2D center_quad;
|
||||
};
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Base/Widget.hpp>
|
||||
#include <JUI/Widgets/Rect.hpp>
|
||||
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
class NineSliceImage
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
|
||||
class NineSliceRect : public Rect, public NineSliceImage
|
||||
{
|
||||
|
||||
};
|
||||
}
|
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;
|
||||
|
||||
};
|
||||
|
||||
}
|
@@ -28,14 +28,18 @@ namespace JUI {
|
||||
|
||||
~Rect() override {}
|
||||
|
||||
bool IsMouseInside() const;
|
||||
|
||||
AABB2D GetActualRenderBounds() const override;
|
||||
|
||||
|
||||
public:
|
||||
void PreDraw() override;
|
||||
void InnerDraw() override;
|
||||
void PostDraw() override;
|
||||
void Draw() override;
|
||||
void Update(float delta) override;
|
||||
|
||||
protected:
|
||||
GLint old_scissor_bounds[4];
|
||||
bool clip_was_enabled;
|
||||
};
|
||||
}
|
@@ -31,11 +31,11 @@
|
||||
& &
|
||||
*/
|
||||
|
||||
namespace RichTextFormat
|
||||
{
|
||||
// TODO: Simpler "One-liner" Rich text, which is a horizontal sequence of textlabels automatically generated from a format.
|
||||
|
||||
class RichTextToken
|
||||
{
|
||||
namespace RichTextFormat {
|
||||
|
||||
class RichTextToken {
|
||||
public:
|
||||
std::string font_face_name;
|
||||
std::string content;
|
||||
|
@@ -20,18 +20,21 @@ namespace JUI
|
||||
Scene();
|
||||
~Scene() override {}
|
||||
void Draw() override;
|
||||
void SetViewportSize(int w, int h);
|
||||
|
||||
Event<Vector2> MouseMoved;
|
||||
|
||||
void GlobalUIScale(const Vector2& value);
|
||||
[[nodiscard]] Vector2 GlobalUIScale() const;
|
||||
|
||||
void Update(float delta) override;
|
||||
|
||||
[[nodiscard]] Vector2 GetAbsolutePosition() const override;
|
||||
|
||||
[[nodiscard]] Vector2 GetAbsoluteSize() const override;
|
||||
|
||||
bool ObserveMouseMovement(const Vector2 &latest_known_pos) override;
|
||||
|
||||
protected:
|
||||
int viewport_w;
|
||||
int viewport_h;
|
||||
Vector2 ui_scale = Vector2(1, 1);
|
||||
};
|
||||
}
|
||||
|
@@ -10,17 +10,91 @@
|
||||
/// @edit 2024-10-11
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <JUI/Widgets/Rect.hpp>
|
||||
namespace JUI {
|
||||
class ScrollingRect;
|
||||
}
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
class ScrollingRect : public Rect
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
Vector2 canvas_size;
|
||||
private:
|
||||
};
|
||||
}
|
||||
// TODO: Fix direction of ScrollBar itself
|
||||
// TODO: MouseWheel scroll, and ScrollBar clickable
|
||||
// TODO: Clamp range of ScrollBar
|
||||
|
||||
/// A Rectangle Widget which has a larger renderable area than the visible area.
|
||||
/// This allows user-controlled scrolling of the viewable content.
|
||||
class JUI::ScrollingRect : public Rect {
|
||||
public:
|
||||
~ScrollingRect() override;
|
||||
|
||||
/// The default constructor initializes all child elements and members to reasonable defaults.
|
||||
ScrollingRect();
|
||||
|
||||
/// Constructs a ScrollingRect by specifying the parent widget to bind it to.
|
||||
explicit ScrollingRect(Widget* parent);
|
||||
public:
|
||||
JGL::RenderTarget* GetCanvas();
|
||||
[[nodiscard]] Vector2i CanvasSize() const;
|
||||
[[nodiscard]] float ScrollPos() const { return scroll; }
|
||||
[[nodiscard]] Vector2 CanvasPosition() const;
|
||||
// TODO scrolling in either direction. Assuming vertical scroll for now.
|
||||
Vector2 CanvasAbsolutePosition() const;
|
||||
Vector2 CanvasAbsoluteSize() const;
|
||||
public:
|
||||
void ScrollPos(float pos) { scroll = pos; }
|
||||
void CanvasSize(const Vector2i& new_size);
|
||||
|
||||
|
||||
/// Sets the scroll amount of this ScrollingRect.
|
||||
/// The value will be clamped between ScrollMinimum() and ScrollMaximum().
|
||||
/// @param value The new scrolling amount to apply.
|
||||
/// @see ScrollMinimum(), ScrollMaximum().
|
||||
void SetScrollAmount(float value);
|
||||
|
||||
/// Scrolls this ScrollingRect by the specified amount.
|
||||
/// @param amount The quantity of pixels by which to scroll the object.
|
||||
/// @see SetScrollAmount()
|
||||
float Scroll(float amount);
|
||||
|
||||
/// @return The amount of pixels this scrolling rect is currently scrolled by.
|
||||
float GetScrollAmount() const;
|
||||
|
||||
[[nodiscard]] bool CanScroll() const;
|
||||
/// The lower-bound of how far up the scroll can be.
|
||||
[[nodiscard]] float ScrollMinimum() const;
|
||||
/// The upper-bound of how far down the scroll can go.
|
||||
[[nodiscard]] float ScrollMaximum() const;
|
||||
|
||||
void SetAutoAdjustScrollAmount(bool value);
|
||||
bool GetAutoAdjustScrollAmount() const;
|
||||
|
||||
void ScrollToTop();
|
||||
|
||||
void ScrollToBottom();
|
||||
|
||||
// TODO: Figure out how to automatically adjust this as child widgets are added, removed, and changed in size.
|
||||
float canvas_height = 0;
|
||||
|
||||
public:
|
||||
void InnerDraw() override;
|
||||
void Draw() override;
|
||||
void Update(float delta) override;
|
||||
|
||||
bool ObserveKeyInput(Key key, bool pressed) override;
|
||||
bool ObserveMouseWheel(int mwheel) override;
|
||||
protected:
|
||||
bool vertical_scrollbar_enabled = true;
|
||||
bool horizontal_scrollbar_enabled = true;
|
||||
bool scrollbar_visible = true;
|
||||
float scrollbar_width = 12;
|
||||
Color4 scrollbar_color = Colors::Whites::Azure;
|
||||
float scroll = 0;
|
||||
float scroll_amount = 10;
|
||||
JGL::RenderTarget* canvas = nullptr;
|
||||
bool auto_adjust_scroll_amount = false;
|
||||
|
||||
protected:
|
||||
/* This isn't public because nothing should ever
|
||||
* have to do this from the outside. -Redacted */
|
||||
void RecomputeRenderTarget();
|
||||
|
||||
void DrawVerticalScrollbar();
|
||||
};
|
24
include/JUI/Widgets/Separator.hpp
Normal file
24
include/JUI/Widgets/Separator.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Base/Widget.hpp>
|
||||
|
||||
namespace JUI {
|
||||
|
||||
enum class Orientation { HORIZONTAL, VERTICAL };
|
||||
|
||||
/// Fills space, and renders a single line through it, based on the given orientation.
|
||||
class Separator : public Widget {
|
||||
public:
|
||||
Separator() : Widget()
|
||||
{
|
||||
Name("Separator");
|
||||
}
|
||||
|
||||
explicit Separator(Widget* parent)
|
||||
{
|
||||
|
||||
}
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
}
|
@@ -31,8 +31,7 @@ namespace JUI
|
||||
public:
|
||||
Event<float> ValueChanged;
|
||||
|
||||
|
||||
Slider() = default;
|
||||
Slider();
|
||||
explicit Slider(JUI::Widget* parent);
|
||||
|
||||
[[nodiscard]] float Minimum() const;
|
||||
@@ -41,7 +40,7 @@ namespace JUI
|
||||
[[nodiscard]] float CurrentValue() const;
|
||||
[[nodiscard]] Color4 ScrubberColor() const;
|
||||
[[nodiscard]] float ScrubberWidth() const;
|
||||
|
||||
[[nodiscard]] bool Dragging() const;
|
||||
[[nodiscard]] float Range() const;
|
||||
|
||||
void Minimum(float min);
|
||||
@@ -50,6 +49,8 @@ namespace JUI
|
||||
void CurrentValue(float value);
|
||||
void ScrubberColor(const Color4& color);
|
||||
void ScrubberWidth(float width);
|
||||
void SetDragging(bool value);
|
||||
|
||||
|
||||
|
||||
void OnClick(const J3ML::LinearAlgebra::Vector2 &MousePos, const JUI::MouseButton &MouseButton) override;
|
||||
@@ -64,10 +65,9 @@ namespace JUI
|
||||
float maximum = 1;
|
||||
float interval = 0.1;
|
||||
float current;
|
||||
bool dragging;
|
||||
// TODO: Later refactor scrubber into a RectBase class?
|
||||
bool dragging = false;
|
||||
float scrubber_width = 20;
|
||||
Color4 scrubber_color;
|
||||
Color4 scrubber_color = Colors::White;
|
||||
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:
|
||||
};
|
||||
}
|
7
include/JUI/Widgets/Table.hpp
Normal file
7
include/JUI/Widgets/Table.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
}
|
@@ -6,8 +6,8 @@
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
/// A Text Widget
|
||||
/// @see TextBase.hpp
|
||||
/// A Widget that displays text within the bounds of it's parent widget.
|
||||
/// @see TextRect.hpp, TextBase.hpp
|
||||
class Text : public Widget, public TextBase
|
||||
{
|
||||
public:
|
||||
|
@@ -9,10 +9,116 @@
|
||||
/// @desc A box that accepts user keyboard input.
|
||||
/// @edit 2024-08-02
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
#include <set>
|
||||
#include <JUI/Mixins/Clickable.hpp>
|
||||
|
||||
#include "TextRect.hpp"
|
||||
|
||||
namespace JUI {
|
||||
|
||||
// TODO: Text Selection
|
||||
// TODO: Support Copy
|
||||
// TODO: Support Paste
|
||||
// TODO: Support Cut
|
||||
// TODO: Support insert at cursor
|
||||
// TODO: Simulate key repeat.
|
||||
|
||||
class TextInputForm : public TextRect, public Clickable {
|
||||
public:
|
||||
Event<> OnSelect;
|
||||
Event<> OnDeselect;
|
||||
Event<std::string> OnReturn;
|
||||
TextInputForm();
|
||||
explicit TextInputForm(Widget* parent);
|
||||
void Update(float elapsed) override;
|
||||
void InnerDraw() override;
|
||||
void Draw() override;
|
||||
|
||||
void MoveCursorLeft();
|
||||
|
||||
void MoveCursorRight();
|
||||
|
||||
/// Returns the maximum position of the cursor, which is determined by the input buffer's length.
|
||||
[[nodiscard]] unsigned int CursorMaxPosition() const;
|
||||
|
||||
void SetCursorPosition(unsigned int pos);
|
||||
|
||||
/// Fires the input event, clears the input buffer, and sets the state to be ready for further input.
|
||||
void SendInput(bool clear_input);
|
||||
|
||||
/// Copy the contents of the input form, or the selection if active, into the the system's clipboard.
|
||||
void Copy();
|
||||
/// Paste the contents of the system's clipboard
|
||||
void Paste();
|
||||
|
||||
void Cut();
|
||||
|
||||
void Backspace();
|
||||
|
||||
void Delete();
|
||||
|
||||
void PushStringToCurrentPlaceInInputBuffer(const std::string &snippet);
|
||||
|
||||
void PushKeyToCurrentPlaceInInputBuffer(const Key &key);
|
||||
|
||||
bool ObserveKeyInput(Key key, bool pressed) override;
|
||||
bool ObserveMouseInput(MouseButton btn, bool pressed) override;
|
||||
bool ObserveMouseMovement(const Vector2 &latest_known_pos) override;
|
||||
[[nodiscard]] std::string GetAutocompleteText() const;
|
||||
void SetAutoCompleteText(const std::string& text);
|
||||
[[nodiscard]] Color4 GetAutocompleteTextColor() const;
|
||||
void SetAutocompleteTextColor(const Color4& color);
|
||||
[[nodiscard]] bool HideAutocompleteOnSelect() const;
|
||||
void SetHideAutocompleteOnSelect(bool hide);
|
||||
[[nodiscard]] bool AutocompleteTextEnabled() const;
|
||||
void SetAutocompleteTextEnabled(bool enabled);
|
||||
|
||||
[[nodiscard]] std::string InputBuffer() const;
|
||||
void SetInputBuffer(const std::string& value);
|
||||
void ClearInputBuffer();
|
||||
|
||||
|
||||
[[nodiscard]] bool HasFocus() const;
|
||||
void SetFocused(bool focused);
|
||||
|
||||
// TODO: Implement procedure to allow API consumer to validate the input, **before** clearing the buffer.
|
||||
|
||||
[[nodiscard]] bool ClearTextOnReturn() const;
|
||||
void ClearTextOnReturn(bool value);
|
||||
|
||||
[[nodiscard]] bool DropFocusOnReturn() const;
|
||||
void DropFocusOnReturn(bool value);
|
||||
|
||||
void GrabFocus();
|
||||
void DropFocus();
|
||||
|
||||
[[nodiscard]] std::set<std::string> GetBlacklist() const;
|
||||
void SetBlacklist(const std::set<std::string>& value);
|
||||
void AddToBlacklist(const std::string& value);
|
||||
|
||||
// TODO: Implement selection of part of input text.
|
||||
Color4 GetSelectionColor() const;
|
||||
void SetSelectionColor(const Color4& color);
|
||||
bool HasSelection() const;
|
||||
std::string GetSelectedText() const;
|
||||
protected:
|
||||
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;
|
||||
private:
|
||||
};
|
||||
}
|
@@ -11,9 +11,17 @@ namespace JUI {
|
||||
TextRect(Widget* parent);
|
||||
void Update(float delta) override;
|
||||
void Draw() override;
|
||||
[[nodiscard]] bool FitText() const { return fit_text; }
|
||||
void FitText(bool on) { fit_text = on; }
|
||||
|
||||
bool AutoFitSizeToText() const { return fit_size_to_text; }
|
||||
void AutoFitSizeToText(bool resize){ fit_size_to_text = resize; }
|
||||
|
||||
|
||||
bool TextWrap() const;
|
||||
void TextWrap(bool enable) const;
|
||||
void InnerDraw() override;
|
||||
protected:
|
||||
bool fit_text = false;
|
||||
|
||||
bool fit_size_to_text = false;
|
||||
bool wrap_text = false;
|
||||
};
|
||||
}
|
43
include/JUI/Widgets/Tooltip.hpp
Normal file
43
include/JUI/Widgets/Tooltip.hpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <JUI/Widgets/TextRect.hpp>
|
||||
#include "ReWindow/InputService.h"
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
class Tooltip : public TextRect
|
||||
{
|
||||
public:
|
||||
Tooltip() : TextRect() {
|
||||
Name("Tooltip");
|
||||
}
|
||||
explicit Tooltip(Widget* parent) : Tooltip()
|
||||
{
|
||||
this->Parent(parent);
|
||||
this->AutoFitSizeToText(true);
|
||||
this->ZIndex(10);
|
||||
this->Visible(false);
|
||||
this->Size({100_px, 20_px});
|
||||
|
||||
}
|
||||
|
||||
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 {};
|
||||
|
||||
protected:
|
||||
};
|
||||
}
|
107
include/JUI/Widgets/UtilityBar.hpp
Normal file
107
include/JUI/Widgets/UtilityBar.hpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
explicit UtilityBar(Widget* parent) : UtilityBar() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
TextButton* AddSubmenu(const std::string& name)
|
||||
{
|
||||
auto btn = AddButton(name);
|
||||
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
private:
|
||||
};
|
||||
}
|
@@ -23,24 +23,21 @@
|
||||
#include <JUI/Mixins/Hoverable.hpp>
|
||||
#include <JUI/Mixins/Clickable.hpp>
|
||||
#include <JUI/Widgets/TextButton.hpp>
|
||||
#include <JUI/Widgets/ImageButton.hpp>
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
namespace JUI {
|
||||
using J3ML::LinearAlgebra::Vector2;
|
||||
|
||||
|
||||
/// TODO: Scope out.
|
||||
class DockingStation {};
|
||||
|
||||
/// A container widget class, with title bar and buttons,
|
||||
/// which can be dragged around, resized, and docked into other applicable widgets.
|
||||
class Window : public Widget, public RectBase, public Clickable, public Hoverable, public Draggable, public Resizable, public Dockable
|
||||
{
|
||||
class Window : public Widget, public RectBase, public Clickable, public Hoverable, public Draggable, public Resizable, public Dockable {
|
||||
public:
|
||||
/// The default constructor sets a default style for this Window.
|
||||
Window();
|
||||
/// Construct a window widget by specifying it's parent.
|
||||
Window(Widget* parent);
|
||||
explicit Window(Widget* parent);
|
||||
|
||||
/// Returns the current size (in x,y pixels) of the Window widget.
|
||||
[[nodiscard]] Vector2 CurrentSize() const;
|
||||
@@ -54,10 +51,24 @@ namespace JUI
|
||||
/// Sets the maximum size (in x,y pixels) that the Window widget is allowed to be.
|
||||
void MaxSize(const Vector2& constraint);
|
||||
|
||||
|
||||
/// Returns the height (in pixels) of the Window's titlebar.
|
||||
// TODO: Decide if this will auto-scale with the titlebar's text height, or the other way around.
|
||||
int TitlebarHeight() const;
|
||||
[[nodiscard]] int TitlebarHeight() const;
|
||||
|
||||
|
||||
/// @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;
|
||||
/// Moves this window down in the window-widget stack.
|
||||
void MoveDown() const;
|
||||
|
||||
void TitlebarHeight(int height);
|
||||
|
||||
/// Returns the text displayed as the Window's title.
|
||||
[[nodiscard]] std::string Title() const;
|
||||
@@ -66,7 +77,7 @@ namespace JUI
|
||||
/// @see class Draggable.
|
||||
bool IsDraggable() const;
|
||||
|
||||
|
||||
/// Sets the window to have the ability to be dragged around by the mouse.
|
||||
void SetDraggable(bool value);
|
||||
|
||||
/// Returns whether this Window is able to be 'Docked' into another widget.
|
||||
@@ -75,29 +86,38 @@ namespace JUI
|
||||
|
||||
void SetDockable(bool value);
|
||||
|
||||
/// Align topbar buttons to the left.
|
||||
void LayoutControlsLeft();
|
||||
|
||||
/// Align topbar buttons to the right.
|
||||
void LayoutControlsRight();
|
||||
|
||||
/// Returns whether resizing the window via right-click is enabled.
|
||||
/// @see class Resizable
|
||||
bool IsResizable() const;
|
||||
|
||||
void SetResizable(bool value);
|
||||
|
||||
void CornerRounding(float radius) override;
|
||||
|
||||
/// Returns a pointer to the Text Widget that is used to render the title bar's text.
|
||||
Text* GetTitleInstance();
|
||||
Text* TitleInstance();
|
||||
/// Returns a pointer to the Rect Widget that is used to layout the title bar contents.
|
||||
Rect* GetTopbarInstance();
|
||||
Rect* TopbarInstance();
|
||||
/// Returns a pointer to the Rect Widget that is used to layout the contents of the window.
|
||||
Rect* GetViewportInstance();
|
||||
Rect* ViewportInstance();
|
||||
/// Returns a pointer to the Exit Button Widget.
|
||||
TextButton* GetExitButtonInstance();
|
||||
ImageButton* ExitButtonInstance();
|
||||
|
||||
Vector2 AbsoluteViewportPosition() const;
|
||||
Vector2 AbsoluteViewportSize() const;
|
||||
AABB2D AbsoluteViewportBounds() const;
|
||||
|
||||
/// Sets the font used by the title-bar text on this Window.
|
||||
void SetTitleFont(const Font& f);
|
||||
|
||||
|
||||
/// Toggles whether this window is actively being dragged by the mouse.
|
||||
/// @see class Draggable.
|
||||
void SetDrag(bool d) override;
|
||||
|
||||
/// @see class Widget.
|
||||
void Update(float delta) override;
|
||||
/// @see class Widget.
|
||||
@@ -106,21 +126,68 @@ namespace JUI
|
||||
void OnClick(const Vector2& m_pos, const MouseButton& m_btn) override;
|
||||
/// @see class Clickable.
|
||||
void OnRelease(const Vector2& m_pos, const MouseButton& m_btn, bool still_hovering) override;
|
||||
|
||||
/// @see class Hoverable. v
|
||||
void OnHover(const J3ML::LinearAlgebra::Vector2 &MousePos) override
|
||||
{
|
||||
Hoverable::OnHover(MousePos);
|
||||
zindex++;
|
||||
focused = true;
|
||||
this->BorderColor(Style::Window::OutlineColor);
|
||||
this->BGColor(Style::Window::OutlineColor);
|
||||
}
|
||||
|
||||
/// @see class Hoverable.
|
||||
void OnExit(const J3ML::LinearAlgebra::Vector2 &MousePos) override {
|
||||
Hoverable::OnExit(MousePos);
|
||||
zindex--;
|
||||
focused = false;
|
||||
this->BorderColor(Style::Window::UnfocusedOutlineColor);
|
||||
this->BGColor(Style::Window::UnfocusedOutlineColor);
|
||||
}
|
||||
|
||||
bool ObserveMouseInput(JUI::MouseButton btn, bool pressed) override
|
||||
{
|
||||
// TODO: Consider how this plays with Clickable::OnClick and Clickable::OnRelease
|
||||
|
||||
if (this->visible && 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();
|
||||
|
||||
protected:
|
||||
JUI::Rect* Topbar;
|
||||
JUI::Rect* Viewport;
|
||||
JUI::Text* TitleLabel;
|
||||
JUI::TextButton* exit_btn;
|
||||
JUI::TextButton* fs_btn;
|
||||
void UpdateInternalWidgetsTitlebarHeight();
|
||||
protected:
|
||||
|
||||
JUI::Rect* Topbar = nullptr;
|
||||
JUI::Rect* Viewport = nullptr;
|
||||
JUI::Text* TitleLabel = nullptr;
|
||||
JUI::ImageButton* exit_btn = nullptr;
|
||||
JUI::ImageButton* fs_btn = nullptr;
|
||||
std::string title = "JUI Window";
|
||||
bool resizable = true;
|
||||
bool focused = false;
|
||||
bool open = false;
|
||||
//bool resizing = false;
|
||||
bool draggable = true;
|
||||
bool dockable = false;
|
||||
int titlebar_height = 16;
|
||||
Vector2 max_size;
|
||||
Vector2 min_size; //= {30, 30};
|
||||
int titlebar_height = Style::Window::TitlebarHeight;
|
||||
int title_font_size = 16;
|
||||
Vector2 max_size = {800, 600};
|
||||
Vector2 min_size = {200, 100}; //= {30, 30};
|
||||
UDim2 size_when_restart_began;
|
||||
};
|
||||
|
||||
void DragTo(const Vector2 &pos);
|
||||
};
|
||||
}
|
||||
|
526
main.cpp
526
main.cpp
@@ -20,203 +20,346 @@
|
||||
#include <JUI/Widgets/TextRect.hpp>
|
||||
#include <JUI/Widgets/Image.hpp>
|
||||
#include <JUI/Widgets/Slider.hpp>
|
||||
#include <rewindow/types/window.h>
|
||||
#include <JUI/Widgets/ScrollingRect.hpp>
|
||||
#include <JUI/Widgets/UtilityBar.hpp>
|
||||
#include <JUI/Widgets/Checkbox.hpp>
|
||||
#include <JUI/Widgets/TextInputForm.hpp>
|
||||
#include <ReWindow/types/Window.h>
|
||||
#include <ReWindow/Logger.h>
|
||||
#include "JUI/Widgets/NineSlice.hpp"
|
||||
#include <JUI/Widgets/Collapsible.hpp>
|
||||
#include <JUI/Widgets/CommandLine.hpp>
|
||||
|
||||
JGL::Font FreeSans;
|
||||
JUI::Scene* scene;
|
||||
JGL::Texture* sample_texture;
|
||||
JGL::Texture* slicer;
|
||||
JUI::VerticalListLayout* list;
|
||||
JUI::ScrollingRect* scroller;
|
||||
JUI::TextRect* widget_count;
|
||||
JUI::CommandLine* console;
|
||||
|
||||
int count_descendants(JUI::Widget* w) {
|
||||
return w->GetDescendants().size();
|
||||
}
|
||||
|
||||
JUI::Scene* CreateScene() {
|
||||
using namespace JUI;
|
||||
|
||||
Scene *root = new Scene();
|
||||
auto *root = new Scene();
|
||||
|
||||
auto* JUI = new TextRect(root);
|
||||
JUI->SetFont(FreeSans);
|
||||
JUI->SetTextSize(48);
|
||||
JUI->SetTextColor({32, 48, 192});
|
||||
JUI->SetContent("Josh User Interface");
|
||||
JUI->AlignBottom();
|
||||
JUI->AlignCenterHorizontally();
|
||||
JUI->AnchorPoint(Vector2(0.5f, 0.5f));
|
||||
JUI->Position({50_percent, 50_percent});
|
||||
//JUI->Size({30_percent, 10_percent});
|
||||
JUI->FitText(true);
|
||||
JUI->Padding(20_px);
|
||||
JUI->BGColor({64, 64, 64, 128});
|
||||
JUI->SetBorderWidth(2);
|
||||
JUI->BorderColor({255, 255, 255, 128});
|
||||
auto* Redacted = new TextRect(root);
|
||||
Redacted->SetFont(FreeSans);
|
||||
Redacted->SetTextSize(32);
|
||||
Redacted->SetTextColor({255, 255, 255});
|
||||
Redacted->SetContent("Redacted Software Group");
|
||||
Redacted->Position({50_percent, 60_percent});
|
||||
//Redacted->Size({30_percent, 10_percent});
|
||||
Redacted->AlignCenterHorizontally();
|
||||
Redacted->AlignTop();
|
||||
Redacted->AnchorPoint(Vector2(0.5f, .5f));
|
||||
Redacted->FitText(true);
|
||||
Redacted->Padding(10_px);
|
||||
Redacted->BGColor({32, 48, 192});
|
||||
Redacted->SetBorderWidth(1);
|
||||
Redacted->BorderColor({64, 64, 64});
|
||||
|
||||
|
||||
// Rect //
|
||||
Rect *rect_element = new Rect(root);
|
||||
rect_element->Name("JimBob");
|
||||
|
||||
//Rect* element = new Rect(root);
|
||||
|
||||
//rect_element->Name("JimBob");
|
||||
//element->BGColor({0,255,0});
|
||||
rect_element->BGColor({0, 64, 0});
|
||||
rect_element->Size({0, 0, 0.1f, 0.2f});
|
||||
rect_element->SetClipsDescendants(true);
|
||||
rect_element->BorderColor({255, 255, 255});
|
||||
rect_element->SetBorderWidth(2.f);
|
||||
|
||||
rect_element->MouseEnter += [rect_element](auto coords) {
|
||||
|
||||
rect_element->BGColor({0, 128, 0});
|
||||
/*root->DescendantAdded += [&] (auto* node) {
|
||||
widget_count->SetContent("Widgets: " + count_descendants(root));
|
||||
};
|
||||
|
||||
rect_element->MouseExit += [rect_element](auto coords) {
|
||||
rect_element->BGColor({0, 64, 0});
|
||||
root->DescendantRemoved += [&] (auto* node) {
|
||||
widget_count->SetContent("Widgets: " + count_descendants(root));
|
||||
};*/
|
||||
|
||||
auto* 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);
|
||||
|
||||
console = new JUI::CommandLine(root);
|
||||
console->Close();
|
||||
|
||||
JUI::UILogs.OnLog += [&] (const std::string& msg, Color4 c){ console->Log(msg, c);};
|
||||
|
||||
auto* topbar = new UtilityBar(root);
|
||||
topbar->ZIndex(3);
|
||||
auto* file = topbar->AddButton("Demos");
|
||||
|
||||
auto* file_tt = new JUI::Tooltip(file);
|
||||
file_tt->SetContent("Tooltip");
|
||||
|
||||
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");
|
||||
|
||||
open_nineslice->OnClickEvent += [&, nineslice_demo_window] (Vector2 pos, JUI::MouseButton btn) {
|
||||
nineslice_demo_window->Visible(true);
|
||||
};
|
||||
|
||||
auto* open_scroll = ctx_menu->AddItem("Scroll Widget Demo");
|
||||
|
||||
open_scroll->OnClickEvent += [&] (Vector2 pos, JUI::MouseButton btn) {};
|
||||
//ctx_menu->AddItem("");
|
||||
|
||||
|
||||
auto* open_console = ctx_menu->AddItem("Command Line");
|
||||
open_console->OnClickEvent += [&] (Vector2 pos, JUI::MouseButton btn)mutable {
|
||||
console->Open();
|
||||
};
|
||||
|
||||
ctx_menu->ZIndex(3);
|
||||
};
|
||||
|
||||
// End Rect //
|
||||
topbar->AddButton("Edit");
|
||||
auto* view = topbar->AddButton("View");
|
||||
|
||||
Slider* slide = new Slider(root);
|
||||
slide->Size({200, 40, 0, 0});
|
||||
slide->BGColor({255, 255, 0});
|
||||
slide->Position({0, 0, 0.125, 0.125});
|
||||
topbar->AddButton("Help");
|
||||
|
||||
// Button //
|
||||
Button *button_element = new Button(root);
|
||||
button_element->Name("BobJim");
|
||||
button_element->BaseBGColor({64, 0, 64});
|
||||
button_element->PressedBGColor({0, 64, 64});
|
||||
button_element->Size({0, 0, 0.1f, 0.1f});
|
||||
button_element->Position({0,0,0.25f,0.25f});
|
||||
//button_element->SetClipsDescendants(true);
|
||||
button_element->BaseBorderColor({255, 255, 255});
|
||||
button_element->SetBorderWidth(2.f);
|
||||
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 bpos = rect_element->Size();
|
||||
//auto* horizontal = new HorizontalListLayout(root);
|
||||
//horizontal->ZIndex(1);
|
||||
|
||||
//exit(1);
|
||||
//button_element->Position(
|
||||
// {bpos.X.Pixels, bpos.Y.Pixels, bpos.X.Scale - 0.2f, 0}); // I don't know how to use sx and sy - maxine
|
||||
//button_element->OnToggleOnEvent += [rect_element] () {
|
||||
// rect_element->BGColor({64, 0, 0});
|
||||
//};
|
||||
//button_element->OnToggleOffEvent += [rect_element] () {
|
||||
// rect_element->BGColor({0, 64, 0});
|
||||
//};
|
||||
//button_element->OnToggleEvent += [button_element] () {
|
||||
// Color4 incbg = button_element->BGColor();
|
||||
// Once an overflow occurs it will reset anyway
|
||||
// Thanks computer science
|
||||
// if (incbg.b < 255)
|
||||
// incbg.b += 10;
|
||||
// button_element->BGColor(incbg);
|
||||
//};
|
||||
Text* btntext = new Text(button_element);
|
||||
btntext->SetContent("I AM BUTTON");
|
||||
btntext->SetFont(FreeSans);
|
||||
btntext->SetTextSize(8);
|
||||
btntext->SetTextColor({255, 0, 0});
|
||||
// End Button //
|
||||
auto* column_rect = new Rect(root);
|
||||
//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);
|
||||
|
||||
// Window //
|
||||
JUI::Window* win_element = new JUI::Window(root);
|
||||
win_element->SetTitleFont(FreeSans);
|
||||
win_element->CornerRounding(5);
|
||||
win_element->Size({50_percent, 50_percent});
|
||||
win_element->SetTitle("JUI Example Window Widget");
|
||||
//win_element->Padding(1_px);
|
||||
auto* column_layout = new VerticalListLayout(column_rect);
|
||||
|
||||
auto* btn_container1 = new Rect(column_layout);
|
||||
btn_container1->Size({100_percent, 24_px});
|
||||
|
||||
auto* btn_h_layout1 = new HorizontalListLayout(btn_container1);
|
||||
btn_h_layout1->Padding({2_px});
|
||||
|
||||
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* 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* 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* btn_container2 = new Rect(column_layout);
|
||||
btn_container2->Size({100_percent, 24_px});
|
||||
btn_container2->BGColor(Colors::DarkGray);
|
||||
|
||||
auto* btn_h_layout2 = new HorizontalListLayout(btn_container2);
|
||||
btn_h_layout2->Padding({2_px});
|
||||
|
||||
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* 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* checkbox_horiz = new HorizontalListLayout(checkbox_container);
|
||||
checkbox_horiz->Padding(2_px);
|
||||
|
||||
auto* label = new TextRect(checkbox_horiz);
|
||||
label->SetContent("Checkboxes");
|
||||
label->BorderWidth(0);
|
||||
label->AutoFitSizeToText(true);
|
||||
|
||||
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* collapsible = new Collapsible(column_layout);
|
||||
collapsible->Size({100_percent, 200_px});
|
||||
|
||||
auto* radio_btn_set = new Rect(collapsible->ContentBox());
|
||||
radio_btn_set->Size({100_percent, 20_px});
|
||||
|
||||
auto* radio_btn_set_layout = new HorizontalListLayout(radio_btn_set);
|
||||
radio_btn_set_layout->Padding(2_px);
|
||||
|
||||
auto* radio_a_btn = new Checkbox(radio_btn_set_layout);
|
||||
radio_a_btn->Size({20_px, 20_px});
|
||||
auto* radio_a_label = new TextRect(radio_btn_set_layout);
|
||||
|
||||
radio_a_label->BorderWidth(0);
|
||||
radio_a_label->Size({20_px, 20_px});
|
||||
radio_a_label->AutoFitSizeToText(true);
|
||||
radio_a_label->SetContent("A ");
|
||||
radio_a_label->SetTextSize(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->SetContent("B ");
|
||||
radio_b_label->SetTextSize(12);
|
||||
|
||||
auto* radio_c_btn = new Checkbox(radio_btn_set_layout);
|
||||
radio_c_btn->Size({20_px, 20_px});
|
||||
auto* radio_c_label = new TextRect(radio_btn_set_layout);
|
||||
|
||||
radio_c_label->BorderWidth(0);
|
||||
radio_c_label->Size({20_px, 20_px});
|
||||
radio_c_label->AutoFitSizeToText(true);
|
||||
radio_c_label->SetContent("C ");
|
||||
radio_c_label->SetTextSize(12);
|
||||
|
||||
//auto* separator_a = new Rect(radio_btn_set_layout());
|
||||
|
||||
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");
|
||||
|
||||
Tween* t = other_window->TweenPosition({50_percent, 50_percent}, {.time = 5});
|
||||
|
||||
t->Completed += [] () { std::cout << "Tween type test!!" << std::endl; };
|
||||
|
||||
scroller = new JUI::ScrollingRect(other_window->ViewportInstance());
|
||||
scroller->Size({100_percent, 100_percent});
|
||||
scroller->BGColor(Colors::Reds::LightCoral);
|
||||
|
||||
list = new JUI::VerticalListLayout(scroller);
|
||||
list->LayoutOrder(JUI::LayoutOrder::V::BOTTOM);
|
||||
|
||||
//nineslice_demo_window->Padding(1_px);
|
||||
// End Window //
|
||||
|
||||
auto darkie = new JUI::Image(win_element->GetViewportInstance(), sample_texture);
|
||||
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 list = new VerticalListLayout(win_element->GetViewportInstance());
|
||||
auto list = new VerticalListLayout(nineslice_demo_window->ViewportInstance());
|
||||
list->Padding(10_px);
|
||||
|
||||
|
||||
|
||||
TextRect* a = new TextRect(list);
|
||||
a->SetTextSize(16);
|
||||
a->SetFont(FreeSans);
|
||||
a->Size({0, 20, 1, 0});
|
||||
//a->FitText(true);
|
||||
a->Center();
|
||||
a->SetContent("Greetings. This is the JUI demo program!");
|
||||
a->SetContent("This is a virtual window.");
|
||||
|
||||
|
||||
TextRect* b = new TextRect(list);
|
||||
b->SetTextSize(16);
|
||||
b->SetContent("JUI is my home-coded game menu building toolkit.");
|
||||
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();
|
||||
b->SetFont(FreeSans);
|
||||
|
||||
TextRect* c = new TextRect(list);
|
||||
c->SetTextSize(16);
|
||||
c->SetContent("Settings");
|
||||
//c->FitText(true);
|
||||
c->Size({0, 20, 1, 0});
|
||||
c->Center();
|
||||
c->SetFont(FreeSans);
|
||||
|
||||
TextRect* d = new TextRect(list);
|
||||
d->SetTextSize(16);
|
||||
d->SetContent("Exit");
|
||||
//d->FitText(true);
|
||||
d->Size({0, 20, 1, 0});
|
||||
d->Center();
|
||||
d->SetFont(FreeSans);
|
||||
|
||||
|
||||
|
||||
|
||||
Text *text = new Text(rect_element);
|
||||
text->SetContent("YO MAMA");
|
||||
text->SetFont(FreeSans);
|
||||
text->SetTextSize(48);
|
||||
text->SetTextColor({255, 0, 0});
|
||||
|
||||
|
||||
|
||||
root->SetViewportSize(800, 600);
|
||||
|
||||
|
||||
|
||||
|
||||
root->SetViewportSize({800, 600});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
class JUIDevelopmentTestWindow : public ReWindow::RWindow {
|
||||
float scale = 1.f;
|
||||
float accum = 0;
|
||||
int iter = 0;
|
||||
|
||||
class JUIDevelopmentTestWindow : public ReWindow::OpenGLWindow {
|
||||
public:
|
||||
|
||||
void initGL() {
|
||||
gladLoadGL();
|
||||
JGL::Update(getSize());
|
||||
JGL::InitTextEngine();
|
||||
auto size = GetSize();
|
||||
auto vec_size = Vector2i(size.x, size.y);
|
||||
bool result = JGL::Init(vec_size, 0.f, 0.f);
|
||||
JGL::Update(vec_size);
|
||||
glClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
|
||||
// TODO: Delete when we update to the next release of JGL
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // NOTE: This MUST be called for text rendering to work properly!!!
|
||||
}
|
||||
|
||||
void Update()
|
||||
void Update(float elapsed)
|
||||
{
|
||||
scene->Update(0.f);
|
||||
|
||||
widget_count->SetContent(std::format("Widgets: {}", count_descendants(scene)));
|
||||
|
||||
using namespace JUI::UDimLiterals;
|
||||
|
||||
accum += elapsed;
|
||||
|
||||
scene->Update(elapsed);
|
||||
|
||||
if (accum > 1.f) {
|
||||
iter--;
|
||||
accum = 0.f;
|
||||
auto* text = new JUI::TextRect(list);
|
||||
text->Size({50_percent, 20_px});
|
||||
text->ZIndex(iter);
|
||||
text->LayoutOrder(-iter);
|
||||
text->SetContent(std::format("{} Sampled Delta: {}ms", -iter, Math::Floor(elapsed*1000.f)));
|
||||
|
||||
scroller->canvas_height += text->GetAbsoluteSize().y;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Draw()
|
||||
@@ -224,17 +367,19 @@ public:
|
||||
scene->Draw();
|
||||
}
|
||||
|
||||
JUIDevelopmentTestWindow(const std::string& title, int w, int h) : ReWindow::RWindow(title, w, h) {}
|
||||
JUIDevelopmentTestWindow(const std::string& title, int w, int h) : ReWindow::OpenGLWindow(title, w, h, 2, 1) {}
|
||||
void OnRefresh(float elapsed) override {
|
||||
Update();
|
||||
JGL::Update(getSize());
|
||||
scene->SetViewportSize(getSize().x, getSize().y);
|
||||
Update(elapsed);
|
||||
auto size = GetSize();
|
||||
Vector2i vSize = Vector2i(size.x, size.y);
|
||||
JGL::Update(vSize);
|
||||
scene->SetViewportSize(Vector2(vSize));
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
Draw();
|
||||
|
||||
this->glSwapBuffers();
|
||||
this->SwapBuffers();
|
||||
}
|
||||
|
||||
|
||||
@@ -244,61 +389,84 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnMouseButtonUp(const ReWindow::WindowEvents::MouseButtonUpEvent &) override
|
||||
void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent &) override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void OnMouseButtonDown(const ReWindow::WindowEvents::MouseButtonDownEvent &) override
|
||||
void OnMouseButtonDown(const ReWindow::MouseButtonDownEvent &) override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void OnMouseMove(const ReWindow::WindowEvents::MouseMoveEvent &) override
|
||||
void OnMouseMove(const ReWindow::MouseMoveEvent &e) override
|
||||
{
|
||||
//JUI::UILogs.Log("Big Stepper");
|
||||
|
||||
Vector2 new_mouse_pos = Vector2(e.Position.x, e.Position.y);
|
||||
|
||||
if (scene->ObserveMouseMovement(new_mouse_pos))
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void OnKeyDown(const ReWindow::KeyDownEvent &) override {
|
||||
|
||||
}
|
||||
|
||||
//bool OnResizeRequest(const ReWindow::WindowResizeRequestEvent &e) override {}
|
||||
JUIDevelopmentTestWindow() : ReWindow::RWindow() {}
|
||||
void OnMouseWheel(const ReWindow::MouseWheelEvent &w) override {
|
||||
scale += w.WheelMovement * 0.125f;
|
||||
|
||||
if (scene->ObserveMouseWheel(w.WheelMovement))
|
||||
return;
|
||||
|
||||
/// As a demo, change the scene's UI scale.
|
||||
scene->GlobalUIScale({scale, scale});
|
||||
}
|
||||
};
|
||||
|
||||
#include <rewindow/logger/logger.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
void inspect_widget(JUI::Widget* w, int depth = 1) {
|
||||
std::cout << std::setw(depth*4);
|
||||
std::cout << w->Name() << std::endl;
|
||||
std::cout << std::setw(0);
|
||||
depth++;
|
||||
for (auto* child : w->GetChildren())
|
||||
{
|
||||
inspect_widget(child, depth);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
using namespace ReWindow;
|
||||
// TODO: Find out new jlog api for silencing specific loggers.
|
||||
|
||||
ReWindow::Logger::Debug.EnableConsole(false);
|
||||
|
||||
auto* window = new JUIDevelopmentTestWindow("Test Window", 800, 600);
|
||||
window->setRenderer(RenderingAPI::OPENGL);
|
||||
//window->SetRenderer(RenderingAPI::OPENGL);
|
||||
window->Open();
|
||||
window->initGL();
|
||||
window->setFullscreen(false);
|
||||
window->setVsyncEnabled(false);
|
||||
window->setResizable(true);
|
||||
window->SetFullscreen(false);
|
||||
window->SetVsyncEnabled(false);
|
||||
window->SetResizable(true);
|
||||
|
||||
JGL::Update({800, 600});
|
||||
|
||||
FreeSans = JGL::Font("assets/fonts/FreeSans.ttf");
|
||||
sample_texture = new JGL::Texture("assets/ld.png");
|
||||
slicer = new JGL::Texture("assets/9slice.png");
|
||||
scene = CreateScene();
|
||||
|
||||
inspect_widget(scene);
|
||||
|
||||
|
||||
window->OnResizeRequestEvent += [&] (ReWindow::WindowResizeRequestEvent e){
|
||||
Vector2 size = e.Size;//window->getLastKnownResize();
|
||||
scene->SetViewportSize(size.x, size.y);
|
||||
Vector2i size = Vector2i(e.Size.x, e.Size.y);//window->getLastKnownResize();
|
||||
scene->SetViewportSize(Vector2(size));
|
||||
std::cout << size.x << "," << size.y << std::endl;
|
||||
JGL::Update(size);
|
||||
};
|
||||
|
||||
window->OnMouseMoveEvent += [&] (MouseMoveEvent e)
|
||||
{
|
||||
scene->ObserveMouseMovement(e.Position);
|
||||
};
|
||||
window->OnMouseMoveEvent += [&] (MouseMoveEvent e) { };
|
||||
|
||||
window->OnMouseButtonUpEvent += [&] (MouseButtonUpEvent e) {
|
||||
/// Invalid operands to binary expression 'MouseButton' and 'const MouseButton'
|
||||
@@ -321,13 +489,17 @@ int main()
|
||||
|
||||
window->OnMouseButtonDownEvent += [&] (MouseButtonDownEvent e) {};
|
||||
|
||||
window->OnKeyDownEvent += [&] (KeyDownEvent e) {};
|
||||
window->OnKeyDownEvent += [&] (KeyDownEvent e) {
|
||||
scene->ObserveKeyInput(e.key, true);
|
||||
};
|
||||
|
||||
window->OnKeyUpEvent += [&] (KeyUpEvent e) {};
|
||||
window->OnKeyUpEvent += [&] (KeyUpEvent e) {
|
||||
scene->ObserveKeyInput(e.key, false);
|
||||
};
|
||||
|
||||
while (window->isAlive()) {
|
||||
window->pollEvents();
|
||||
window->refresh();
|
||||
while (window->IsAlive()) {
|
||||
//window->PollEvents();
|
||||
window->ManagedRefresh();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
BIN
showcase.png
BIN
showcase.png
Binary file not shown.
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 136 KiB |
@@ -1,15 +1,33 @@
|
||||
#include <JUI/Base/ImageBase.hpp>
|
||||
#include <JGL/JGL.h>
|
||||
#include <JUI/JUI.hpp>
|
||||
|
||||
using namespace JGL;
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
void ImageBase::Draw(const Vector2 &pos, const Vector2 &size) {
|
||||
J2D::Begin();
|
||||
void ImageBase::Draw(const Vector2 &pos) {
|
||||
if (texture == nullptr) {
|
||||
UILogs("Attempt to draw ImageBase that has nullptr texture!");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Support image rotation in the widget.
|
||||
J2D::DrawSprite(*texture, pos, 0, origin, scale, image_color);
|
||||
J2D::End();
|
||||
|
||||
}
|
||||
|
||||
void ImageBase::Draw(const Vector2 &pos, const Vector2 &size) {
|
||||
if (texture == nullptr) {
|
||||
UILogs("Attempt to draw ImageBase that has nullptr texture!");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 overridden_scale = size / Vector2(texture->GetDimensions());
|
||||
|
||||
|
||||
// TODO: Support image rotation in the widget.
|
||||
J2D::DrawSprite(*texture, pos, 0, origin, overridden_scale, image_color);
|
||||
}
|
||||
|
||||
ImageBase::ImageBase()
|
||||
@@ -39,5 +57,7 @@ namespace JUI
|
||||
Vector2 ImageBase::Scale() const { return scale;}
|
||||
|
||||
Vector2 ImageBase::Origin() const { return origin;}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@@ -4,40 +4,43 @@
|
||||
using namespace JGL;
|
||||
|
||||
namespace JUI {
|
||||
RectBase::RectBase() {}
|
||||
RectBase::RectBase() = default;
|
||||
|
||||
bool RectBase::GetClipsDescendants() const { return clips_descendants;}
|
||||
|
||||
void RectBase::SetClipsDescendants(bool clipping) { clips_descendants = clipping;}
|
||||
void RectBase::SetClipsDescendants(bool clipping) { clips_descendants = clipping; }
|
||||
|
||||
void RectBase::BGColor(const Color4 &col) { bg_color = col;}
|
||||
void RectBase::BGColor(const Color4 &col) { bg_color = col; }
|
||||
|
||||
void RectBase::BorderColor(const Color4 &col) { border_color = col;}
|
||||
void RectBase::BorderColor(const Color4 &col) { border_color = col; }
|
||||
|
||||
void RectBase::SetBorderWidth(float w) {border_width = w;}
|
||||
void RectBase::BorderWidth(float w) { border_width = w; }
|
||||
|
||||
float RectBase::GetBorderWidth() const { return border_width;}
|
||||
float RectBase::GetBorderWidth() const { return border_width; }
|
||||
|
||||
Color4 RectBase::GetBorderColor() const { return border_color;}
|
||||
Color4 RectBase::GetBorderColor() const { return border_color; }
|
||||
|
||||
Color4 RectBase::BGColor() const { return bg_color; }
|
||||
|
||||
void RectBase::SetBorderStyling(const Color4 &color, float width) {
|
||||
BorderColor(color);
|
||||
SetBorderWidth(width);
|
||||
BorderWidth(width);
|
||||
}
|
||||
|
||||
void RectBase::Draw(const Vector2 &abs_pos, const Vector2 &abs_size) {
|
||||
|
||||
void RectBase::Draw(const Vector2& abs_pos, const Vector2& abs_size) {
|
||||
Draw(bg_color, border_color, abs_pos, abs_size);
|
||||
}
|
||||
|
||||
void RectBase::Draw(const Color4& bgColor, const Color4& borderColor, const Vector2 &abs_pos, const Vector2 &abs_size) {
|
||||
|
||||
// Background rect
|
||||
J2D::Begin();
|
||||
|
||||
if (corner_rounding_radius > 0)
|
||||
J2D::FillRoundedRect(bg_color, abs_pos, abs_size, corner_rounding_radius);
|
||||
J2D::FillRoundedRect(bgColor, abs_pos, abs_size, corner_rounding_radius);
|
||||
else
|
||||
J2D::FillRect(bg_color, abs_pos, abs_size);
|
||||
|
||||
// Outline rect
|
||||
J2D::FillRect(bgColor, abs_pos, abs_size);
|
||||
|
||||
// Outline rect - compute the size change to fit the border accurately.
|
||||
Vector2 border_offset = {0, 0};
|
||||
|
||||
if (border_mode == BorderMode::Inset)
|
||||
@@ -47,19 +50,14 @@ namespace JUI {
|
||||
if (border_mode == BorderMode::Outline)
|
||||
border_offset = {border_width/2.f, border_width/2.f};
|
||||
|
||||
|
||||
// TODO: implement border_mode behavior when rendering here.
|
||||
|
||||
if (border_width > 0)
|
||||
{
|
||||
// Draw the outline.
|
||||
if (border_width > 0) {
|
||||
if (corner_rounding_radius > 0)
|
||||
J2D::OutlineRoundedRect(border_color, abs_pos - border_offset, abs_size + (border_offset*2), corner_rounding_radius, border_width);
|
||||
J2D::OutlineRoundedRect(borderColor, abs_pos - border_offset, abs_size + (border_offset*2), corner_rounding_radius, border_width);
|
||||
else
|
||||
J2D::OutlineRect(border_color, abs_pos - border_offset, abs_size + (border_offset*2), border_width);
|
||||
J2D::OutlineRect(borderColor, abs_pos - border_offset, abs_size + (border_offset*2), border_width);
|
||||
}
|
||||
|
||||
|
||||
J2D::End();
|
||||
}
|
||||
|
||||
void RectBase::BorderMode(const enum BorderMode &mode) {
|
||||
|
@@ -1,124 +1,144 @@
|
||||
#include <JUI/Base/TextBase.hpp>
|
||||
#include "JGL/JGL.h"
|
||||
#include <JGL/JGL.h>
|
||||
using namespace JUI;
|
||||
|
||||
void TextBase::SetContent(const std::string& content) { this->content = content; state_redraw = true; }
|
||||
|
||||
namespace JUI {
|
||||
void TextBase::SetContent(const std::string &content) { this->content = content; }
|
||||
void TextBase::SetTextColor(const Color4& color) { this->text_color = color; state_redraw = true; }
|
||||
|
||||
void TextBase::SetTextColor(const Color4 &color) { this->text_color = color; }
|
||||
void TextBase::SetOutlineColor(const Color4& color) { this->outline_color = color; }
|
||||
|
||||
void TextBase::SetOutlineColor(const Color4 &color) { this->outline_color = color; }
|
||||
void TextBase::SetHorizontalTextAlign(const TextAlign::H& align) { this->h_align = align;}
|
||||
|
||||
void TextBase::SetHorizontalTextAlign(const TextAlign::H &align) { this->h_align = align;}
|
||||
void TextBase::SetVerticalTextAlign(const TextAlign::V& align) { this->v_align = align; }
|
||||
|
||||
void TextBase::SetVerticalTextAlign(const TextAlign::V &align) { this->v_align = align; }
|
||||
|
||||
void TextBase::SetTextAlign(const TextAlign::H &h_align, const TextAlign::V &v_align) {
|
||||
SetHorizontalTextAlign(h_align);
|
||||
SetVerticalTextAlign(v_align);
|
||||
}
|
||||
|
||||
void TextBase::AlignLeft() { SetHorizontalTextAlign(TextAlign::H::Left);}
|
||||
|
||||
void TextBase::AlignRight() { SetHorizontalTextAlign(TextAlign::H::Right);}
|
||||
|
||||
void TextBase::AlignTop() {SetVerticalTextAlign(TextAlign::V::Top);}
|
||||
|
||||
void TextBase::AlignBottom() {SetVerticalTextAlign(TextAlign::V::Bottom);}
|
||||
|
||||
void TextBase::AlignCenterBoth() {
|
||||
SetVerticalTextAlign(TextAlign::V::Center);
|
||||
SetHorizontalTextAlign(TextAlign::H::Center);
|
||||
}
|
||||
|
||||
void TextBase::Center() { AlignCenterBoth(); }
|
||||
|
||||
void TextBase::AlignCenterHorizontally() {
|
||||
SetHorizontalTextAlign(TextAlign::H::Center);
|
||||
}
|
||||
|
||||
void TextBase::AlignCenterVertically() {
|
||||
SetVerticalTextAlign(TextAlign::V::Center);
|
||||
}
|
||||
|
||||
void TextBase::SetWordWrap(bool wrap) { word_wrap = wrap; }
|
||||
|
||||
void TextBase::Draw(const Vector2 &abs_pos, const Vector2 &abs_size) {
|
||||
|
||||
// Calculate how much to origin the text based on alignment.
|
||||
float align_x = abs_pos.x;
|
||||
float align_y = abs_pos.y;
|
||||
|
||||
auto bounds = this->GetFont().MeasureString(this->content, this->text_size);
|
||||
|
||||
if (h_align == TextAlign::H::Left) {
|
||||
// Render from left-side of boundary.
|
||||
align_x = abs_pos.x;
|
||||
}
|
||||
if (h_align == TextAlign::H::Center) {
|
||||
// Render from horizontal center, origin by half of the text's width.
|
||||
align_x = abs_pos.x + (abs_size.x / 2) - (bounds.x / 2);
|
||||
}
|
||||
if (h_align == TextAlign::H::Right) {
|
||||
// Render from right-side of boundary, origin by text width.
|
||||
align_x = abs_pos.x + abs_size.x - bounds.x;
|
||||
}
|
||||
if (v_align == TextAlign::V::Top) {
|
||||
// Render from top of boundary.
|
||||
align_y = abs_pos.y;
|
||||
}
|
||||
if (v_align == TextAlign::V::Center) {
|
||||
// Render from vertical center, origin by half of the text's height.
|
||||
align_y = abs_pos.y + (abs_size.y / 2) - (bounds.y / 2);
|
||||
}
|
||||
if (v_align == TextAlign::V::Bottom) {
|
||||
// Render from bottom of boundary, origin by the text's height.
|
||||
align_y = abs_pos.y + abs_size.y - bounds.y;
|
||||
}
|
||||
|
||||
// Now we render.
|
||||
float scale = 1.f;
|
||||
|
||||
JGL::J2D::Begin();
|
||||
JGL::J2D::DrawString(
|
||||
this->text_color,
|
||||
this->content,
|
||||
align_x, align_y,
|
||||
scale,
|
||||
this->text_size,
|
||||
this->set_font);
|
||||
JGL::J2D::End();
|
||||
|
||||
}
|
||||
|
||||
std::string TextBase::GetContent() const { return content;}
|
||||
|
||||
Color4 TextBase::GetTextColor() const { return text_color;}
|
||||
|
||||
Color4 TextBase::GetOutlineColor() const { return outline_color;}
|
||||
|
||||
TextAlign::H TextBase::GetHorizontalTextAlign() const { return h_align;}
|
||||
|
||||
TextAlign::V TextBase::GetVerticalTextAlign() const {return v_align;}
|
||||
|
||||
Vector2 TextBase::GetTextBounds() const { return GetFont().MeasureString(content, text_size); }
|
||||
|
||||
JGL::Font TextBase::GetFont() const { return set_font;}
|
||||
|
||||
u32 TextBase::GetTextSize() const { return text_size; }
|
||||
|
||||
void TextBase::SetFont(const JGL::Font &font) {
|
||||
set_font = font;
|
||||
}
|
||||
|
||||
TextBase::TextBase() {}
|
||||
|
||||
void TextBase::SetTextSize(u32 size) {
|
||||
text_size = size;
|
||||
}
|
||||
|
||||
void TextBase::Update(float delta) {}
|
||||
void TextBase::SetTextAlign(const TextAlign::H& h_align, const TextAlign::V& v_align) {
|
||||
SetHorizontalTextAlign(h_align);
|
||||
SetVerticalTextAlign(v_align);
|
||||
}
|
||||
|
||||
void TextBase::AlignLeft() { SetHorizontalTextAlign(TextAlign::H::Left); }
|
||||
|
||||
void TextBase::AlignRight() { SetHorizontalTextAlign(TextAlign::H::Right); }
|
||||
|
||||
void TextBase::AlignTop() { SetVerticalTextAlign(TextAlign::V::Top); }
|
||||
|
||||
void TextBase::AlignBottom() { SetVerticalTextAlign(TextAlign::V::Bottom); }
|
||||
|
||||
void TextBase::AlignCenterBoth() {
|
||||
SetVerticalTextAlign(TextAlign::V::Center);
|
||||
SetHorizontalTextAlign(TextAlign::H::Center);
|
||||
}
|
||||
|
||||
void TextBase::Center() { AlignCenterBoth(); }
|
||||
|
||||
void TextBase::AlignCenterHorizontally() {
|
||||
SetHorizontalTextAlign(TextAlign::H::Center);
|
||||
}
|
||||
|
||||
void TextBase::AlignCenterVertically() {
|
||||
SetVerticalTextAlign(TextAlign::V::Center);
|
||||
}
|
||||
|
||||
void TextBase::SetWordWrap(bool wrap) { word_wrap = wrap; }
|
||||
|
||||
void TextBase::Draw(const Vector2& abs_pos, const Vector2& abs_size, const std::string& content, unsigned int size, const Color4& color) {
|
||||
// Calculate how much to origin the text based on alignment.
|
||||
float align_x = abs_pos.x;
|
||||
float align_y = abs_pos.y;
|
||||
|
||||
std::string start = content;
|
||||
std::vector<std::string> lines;
|
||||
|
||||
|
||||
/*for (int i = 0; i < start.length(); i++) {
|
||||
std::string substr = start.substr(0, i);
|
||||
auto sub_bounds = this->GetFont().MeasureString(substr, size);
|
||||
|
||||
if (sub_bounds.x > abs_size.x)
|
||||
|
||||
|
||||
|
||||
}*/
|
||||
|
||||
|
||||
auto bounds = this->GetFont().MeasureString(content, size);
|
||||
// Do nothing if there is no text.
|
||||
if (bounds.x == 0 || bounds.y == 0)
|
||||
return;
|
||||
|
||||
// Render from left-side of boundary.
|
||||
if (h_align == TextAlign::H::Left)
|
||||
align_x = abs_pos.x;
|
||||
// Render from horizontal center, origin by half of the text's width.
|
||||
if (h_align == TextAlign::H::Center)
|
||||
align_x = abs_pos.x + (abs_size.x / 2) - (bounds.x / 2);
|
||||
// Render from right-side of boundary, origin by text width.
|
||||
if (h_align == TextAlign::H::Right)
|
||||
align_x = abs_pos.x + abs_size.x - bounds.x;
|
||||
// Render from top of boundary.
|
||||
if (v_align == TextAlign::V::Top)
|
||||
align_y = abs_pos.y;
|
||||
// Render from vertical center, origin by half of the text's height.
|
||||
if (v_align == TextAlign::V::Center)
|
||||
align_y = abs_pos.y + (abs_size.y / 2) - (bounds.y / 2);
|
||||
// Render from bottom of boundary, origin by the text's height.
|
||||
if (v_align == TextAlign::V::Bottom)
|
||||
align_y = abs_pos.y + abs_size.y - bounds.y;
|
||||
|
||||
float scale = 1.f;
|
||||
bool use_render_target = false;
|
||||
|
||||
// Has to be checked because this function lets you pass in parameters instead
|
||||
// of using the ones we have saved. Which again, defeats the point. - Redacted.
|
||||
if (content == this->content) {
|
||||
// if Render Target needs updating.
|
||||
if (state_redraw) {
|
||||
text_canvas->Resize({(int) bounds.x, (int) bounds.y});
|
||||
J2D::Begin(text_canvas, true);
|
||||
J2D::DrawString(color, content, 0, 0, scale, size, this->set_font);
|
||||
J2D::End();
|
||||
state_redraw = false;
|
||||
}
|
||||
use_render_target = true;
|
||||
}
|
||||
|
||||
Vector2 text_pos = {align_x, align_y};
|
||||
//J2D::Begin();
|
||||
//J2D::OutlineRect(color, text_pos, bounds); // Draw bounding box for debugging.
|
||||
use_render_target ? J2D::DrawRenderTarget(text_canvas, {align_x, align_y})
|
||||
: J2D::DrawString(color, content, align_x, align_y, scale, size, this->set_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; }
|
||||
|
||||
Color4 TextBase::GetTextColor() const { return text_color; }
|
||||
|
||||
Color4 TextBase::GetOutlineColor() const { return outline_color; }
|
||||
|
||||
TextAlign::H TextBase::GetHorizontalTextAlign() const { return h_align; }
|
||||
|
||||
TextAlign::V TextBase::GetVerticalTextAlign() const { return v_align; }
|
||||
|
||||
Vector2 TextBase::GetTextBounds() { return GetFont().MeasureString(content, text_size); }
|
||||
|
||||
JGL::Font TextBase::GetFont() const { return set_font; }
|
||||
|
||||
u32 TextBase::GetTextSize() const { return text_size; }
|
||||
|
||||
void TextBase::SetFont(const JGL::Font& font) {
|
||||
set_font = font;
|
||||
state_redraw = true;
|
||||
}
|
||||
|
||||
void TextBase::SetTextSize(u32 size) {
|
||||
text_size = size;
|
||||
state_redraw = true;
|
||||
}
|
||||
|
||||
void TextBase::Update(float delta) {}
|
@@ -2,343 +2,631 @@
|
||||
#include <jlog/Logger.hpp>
|
||||
#include <J3ML/Geometry/AABB2D.hpp>
|
||||
|
||||
namespace JUI {
|
||||
using namespace JUI;
|
||||
|
||||
Widget::Widget() {
|
||||
name = "Widget";
|
||||
children = std::vector<Widget *>();
|
||||
}
|
||||
Widget::Widget() {
|
||||
name = "Widget";
|
||||
children = std::vector<Widget *>();
|
||||
}
|
||||
Widget::Widget(Widget* parent) : Widget() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
Widget::Widget(Widget* parent) : Widget()
|
||||
{
|
||||
this->Parent(parent);
|
||||
}
|
||||
void Widget::Parent(Widget* newParent) {
|
||||
// hold a reference to this so it doesn't get collected as we're working.
|
||||
Widget* oldParent = this->parent;
|
||||
|
||||
void Widget::Parent(Widget* newParent) {
|
||||
// hold a reference to this so it doesn't get collected as we're working.
|
||||
Widget* oldParent = this->parent;
|
||||
// New parent is old parent, do nothing, maybe raise a warning.
|
||||
if (newParent == oldParent)
|
||||
return;
|
||||
|
||||
// New parent is old parent, do nothing, maybe raise a warning.
|
||||
if (newParent == oldParent)
|
||||
return;
|
||||
// Don't allow for an instance to be parented to itself
|
||||
if (this == newParent)
|
||||
throw std::runtime_error("Cannot parent a widget to itself.");
|
||||
// You're trying to break the linearity of the object hierarchy by creating a circle in the graph.
|
||||
if (this->IsAncestorOf(newParent))
|
||||
throw std::runtime_error("Cannot create circular reference.");
|
||||
|
||||
// Don't allow for an instance to be parented to itself
|
||||
if (this == newParent)
|
||||
throw std::runtime_error("Cannot parent a widget to itself.");
|
||||
// You're trying to break the linearity of the object hierarchy by creating a circle in the graph.
|
||||
if (this->IsAncestorOf(newParent))
|
||||
throw std::runtime_error("Cannot create circular reference.");
|
||||
|
||||
for (Widget* ancestor: this->GetAncestors()) {
|
||||
if (oldParent && !ancestor->IsAncestorOf(newParent) && newParent != ancestor) {
|
||||
ancestor->DescendantRemoved(this);
|
||||
for (Widget* descendant : this->GetDescendants()) {
|
||||
ancestor->DescendantRemoved(descendant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove ourselves from our parent (if we have one)
|
||||
if (this->parent) {
|
||||
// this->parent->ChildRemoved(this)
|
||||
std::erase(newParent->children, this);
|
||||
}
|
||||
|
||||
// Update our old parent to the new one
|
||||
this->parent = newParent;
|
||||
|
||||
// If our parent is set to nullptr, we can't update it's vector of children
|
||||
if (!newParent) return;
|
||||
|
||||
// Add ourselves to our new parent's list of children
|
||||
newParent->children.emplace_back(this);
|
||||
//newParent->ChildAdded(this);
|
||||
|
||||
for (Widget* ancestor : this->GetAncestors()) {
|
||||
if (!oldParent || (!oldParent->IsDescendantOf(newParent) && oldParent != ancestor)) {
|
||||
// Don't fire unless an instance is actually a new descendant
|
||||
ancestor->DescendantAdded(this);
|
||||
for (Widget* descendant: this->GetDescendants()) {
|
||||
ancestor->DescendantAdded(descendant);
|
||||
}
|
||||
}
|
||||
for (Widget* ancestor: this->GetAncestors()) {
|
||||
if (oldParent && !ancestor->IsAncestorOf(newParent) && newParent != ancestor) {
|
||||
ancestor->DescendantRemoved(this);
|
||||
for (Widget* descendant : this->GetDescendants())
|
||||
ancestor->DescendantRemoved(descendant);
|
||||
}
|
||||
}
|
||||
|
||||
bool Widget::IsDescendantOf(Widget* ancestor) {
|
||||
if (ancestor == nullptr) return false;
|
||||
// Remove ourselves from our parent (if we have one)
|
||||
if (this->parent)
|
||||
std::erase(this->parent->children, this);
|
||||
|
||||
Widget* instance = this;
|
||||
while (instance->GetParent()) {
|
||||
instance = instance->GetParent();
|
||||
if (instance == ancestor)
|
||||
return true;
|
||||
// Update our old parent to the new one
|
||||
this->parent = newParent;
|
||||
|
||||
// If our parent is set to nullptr, we can't update it's vector of children
|
||||
if (!newParent) return;
|
||||
|
||||
// Add ourselves to our new parent's list of children
|
||||
newParent->children.emplace_back(this);
|
||||
//newParent->ChildAdded(this);
|
||||
|
||||
for (Widget* ancestor : this->GetAncestors()) {
|
||||
if (!oldParent || (!oldParent->IsDescendantOf(newParent) && oldParent != ancestor)) {
|
||||
// Don't fire unless an instance is actually a new descendant
|
||||
ancestor->DescendantAdded(this);
|
||||
for (Widget* descendant: this->GetDescendants())
|
||||
ancestor->DescendantAdded(descendant);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Widget::IsAncestorOf(Widget* descendant) {
|
||||
if (descendant == nullptr) return false;
|
||||
Widget* instance = descendant;
|
||||
while (instance->GetParent()) {
|
||||
instance = instance->GetParent();
|
||||
if (instance == this)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
bool Widget::IsDescendantOf(Widget* ancestor) {
|
||||
if (ancestor == nullptr) return false;
|
||||
|
||||
Widget* instance = this;
|
||||
while (instance->GetParent()) {
|
||||
instance = instance->GetParent();
|
||||
if (instance == ancestor)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector2 Widget::AnchorPoint() const {
|
||||
return anchor_point;
|
||||
bool Widget::IsAncestorOf(Widget* descendant) {
|
||||
if (descendant == nullptr) return false;
|
||||
Widget* instance = descendant;
|
||||
while (instance->GetParent()) {
|
||||
instance = instance->GetParent();
|
||||
if (instance == this)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UDim Widget::PaddingLeft() const { return pad_left;}
|
||||
UDim Widget::PaddingTop() const { return pad_top;}
|
||||
UDim Widget::PaddingRight() const { return pad_right;}
|
||||
UDim Widget::PaddingBottom() const { return pad_bottom;}
|
||||
Vector2 Widget::AnchorPoint() const {
|
||||
return anchor_point;
|
||||
}
|
||||
|
||||
void Widget::PaddingLeft (const UDim& pad_left) { this->pad_left = pad_left; }
|
||||
void Widget::PaddingTop (const UDim& pad_top) { this->pad_top = pad_top; }
|
||||
void Widget::PaddingRight (const UDim& pad_right) { this->pad_right = pad_right; }
|
||||
void Widget::PaddingBottom(const UDim& pad_bottom) { this->pad_bottom = pad_bottom; }
|
||||
UDim Widget::PaddingLeft() const { return pad_left;}
|
||||
UDim Widget::PaddingTop() const { return pad_top;}
|
||||
UDim Widget::PaddingRight() const { return pad_right;}
|
||||
UDim Widget::PaddingBottom() const { return pad_bottom;}
|
||||
|
||||
void Widget::Padding(const UDim &left, const UDim &top, const UDim &right, const UDim &bottom) {
|
||||
PaddingLeft(left);
|
||||
PaddingTop(top);
|
||||
PaddingRight(right);
|
||||
PaddingBottom(bottom);
|
||||
void Widget::PaddingLeft (const UDim& value) { this->pad_left = value; }
|
||||
void Widget::PaddingTop (const UDim& pad_top) { this->pad_top = pad_top; }
|
||||
void Widget::PaddingRight (const UDim& pad_right) { this->pad_right = pad_right; }
|
||||
void Widget::PaddingBottom(const UDim& pad_bottom) { this->pad_bottom = pad_bottom; }
|
||||
|
||||
void Widget::Padding(const UDim &left, const UDim &top, const UDim &right, const UDim &bottom) {
|
||||
PaddingLeft(left);
|
||||
PaddingTop(top);
|
||||
PaddingRight(right);
|
||||
PaddingBottom(bottom);
|
||||
}
|
||||
|
||||
void Widget::Padding(const UDim &padding) {
|
||||
Padding(padding, padding, padding, padding);
|
||||
}
|
||||
|
||||
UDim Widget::MarginLeft () const { return margin_left;}
|
||||
UDim Widget::MarginTop () const { return margin_top;}
|
||||
UDim Widget::MarginRight () const { return margin_right;}
|
||||
UDim Widget::MarginBottom() const { return margin_bottom;}
|
||||
|
||||
void Widget::MarginLeft(const UDim& ml) { margin_left = ml;}
|
||||
void Widget::MarginTop(const UDim& mt) { margin_top = mt;}
|
||||
void Widget::MarginRight(const UDim& mr) { margin_right = mr;}
|
||||
void Widget::MarginBottom(const UDim& mb) { margin_bottom = mb;}
|
||||
|
||||
void Widget::Margin(const UDim &left, const UDim &top, const UDim &right, const UDim &bottom) {
|
||||
MarginLeft(left);
|
||||
MarginTop(top);
|
||||
MarginRight(right);
|
||||
MarginBottom(bottom);
|
||||
}
|
||||
|
||||
void Widget::Margin(const UDim &margin) {
|
||||
Margin(margin, margin, margin, margin);
|
||||
}
|
||||
|
||||
std::string Widget::Name() const { return name; }
|
||||
void Widget::Name(const std::string& new_name) { name = new_name;}
|
||||
|
||||
bool Widget::IsVisible() const { return visible; }
|
||||
void Widget::Visible(bool enabled) { visible = enabled;}
|
||||
|
||||
Widget* Widget::GetParent() const { return parent; }
|
||||
|
||||
UDim2 Widget::Position() const { return position; }
|
||||
void Widget::Position(const UDim2& pos) { position = pos; }
|
||||
|
||||
UDim2 Widget::Size() const { return size;}
|
||||
void Widget::Size(const UDim2& s) { size = s; }
|
||||
|
||||
float Widget::GetAbsoluteRotation() const {
|
||||
// TODO: implement rotation correctly!!
|
||||
return rotation;
|
||||
}
|
||||
|
||||
std::vector<Widget *> Widget::GetChildren() { return children; }
|
||||
|
||||
std::vector<Widget *> Widget::GetDescendants() {
|
||||
std::vector<Widget*> descendants;
|
||||
for (auto& child: this->GetChildren()) {
|
||||
descendants.push_back(child);
|
||||
std::vector<Widget*> recursiveDescendants = child->GetDescendants();
|
||||
descendants.insert(descendants.end(), recursiveDescendants.begin(), recursiveDescendants.end());
|
||||
}
|
||||
return descendants;
|
||||
}
|
||||
|
||||
void Widget::Padding(const UDim &padding) {
|
||||
Padding(padding, padding, padding, padding);
|
||||
}
|
||||
std::vector<Widget*> Widget::GetAncestors() {
|
||||
std::vector<Widget*> ancestors;
|
||||
for (Widget *ancestor = this->parent; ancestor; ancestor = ancestor->parent)
|
||||
ancestors.push_back(ancestor);
|
||||
return ancestors;
|
||||
}
|
||||
|
||||
UDim Widget::MarginLeft () const { return margin_left;}
|
||||
UDim Widget::MarginTop () const { return margin_top;}
|
||||
UDim Widget::MarginRight () const { return margin_right;}
|
||||
UDim Widget::MarginBottom() const { return margin_bottom;}
|
||||
|
||||
void Widget::MarginLeft(const UDim& ml) { margin_left = ml;}
|
||||
void Widget::MarginTop(const UDim& mt) { margin_top = mt;}
|
||||
void Widget::MarginRight(const UDim& mr) { margin_right = mr;}
|
||||
void Widget::MarginBottom(const UDim& mb) { margin_bottom = mb;}
|
||||
|
||||
void Widget::Margin(const UDim &left, const UDim &top, const UDim &right, const UDim &bottom) {
|
||||
MarginLeft(left);
|
||||
MarginTop(top);
|
||||
MarginRight(right);
|
||||
MarginBottom(bottom);
|
||||
}
|
||||
|
||||
void Widget::Margin(const UDim &margin) {
|
||||
Margin(margin, margin, margin, margin);
|
||||
}
|
||||
|
||||
std::string Widget::Name() const { return name; }
|
||||
void Widget::Name(const std::string& new_name) { name = new_name;}
|
||||
|
||||
bool Widget::IsVisible() const { return visible; }
|
||||
void Widget::Visible(bool enabled) { visible = enabled;}
|
||||
|
||||
Widget* Widget::GetParent() const {
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
UDim2 Widget::Position() const { return position; }
|
||||
void Widget::Position(const UDim2& pos) { position = pos; }
|
||||
|
||||
UDim2 Widget::Size() const { return size;}
|
||||
void Widget::Size(const UDim2& s) { size = s; }
|
||||
|
||||
float Widget::GetAbsoluteRotation() const {
|
||||
// TODO: implement rotation correctly!!
|
||||
return rotation;
|
||||
}
|
||||
|
||||
|
||||
std::vector<Widget *> Widget::GetChildren() { return children; }
|
||||
|
||||
std::vector<Widget *> Widget::GetDescendants() {
|
||||
std::vector<Widget*> descendants;
|
||||
for (auto& child: this->GetChildren()) {
|
||||
descendants.push_back(child);
|
||||
std::vector<Widget*> recursiveDescendants = child->GetDescendants();
|
||||
descendants.insert(descendants.end(), recursiveDescendants.begin(), recursiveDescendants.end());
|
||||
}
|
||||
return descendants;
|
||||
}
|
||||
|
||||
std::vector<Widget*> Widget::GetAncestors() {
|
||||
std::vector<Widget*> ancestors;
|
||||
for (Widget *ancestor = this->parent; ancestor; ancestor = ancestor->parent) {
|
||||
ancestors.push_back(ancestor);
|
||||
}
|
||||
return ancestors;
|
||||
}
|
||||
/*float Widget::GetAbsoluteMarginLeft() {}
|
||||
float Widget::GetAbsoluteMarginRight() {}
|
||||
float Widget::GetAbsoluteMarginTop() {}
|
||||
float Widget::GetAbsoluteMarginBottom() {}
|
||||
float Widget::GetAbsolutePaddingLeft() {}
|
||||
float Widget::GetAbsolutePaddingRight() {}
|
||||
float Widget::GetAbsolutePaddingTop() {}
|
||||
float Widget::GetAbsolutePaddingBottom() {}*/
|
||||
|
||||
|
||||
|
||||
Vector2 Widget::GetAbsolutePosition() const {
|
||||
auto child_pos_scale = this->Position().GetScale();
|
||||
auto child_pos_pixels = this->Position().GetPixels();
|
||||
auto parent_abs_size = this->GetParent()->GetAbsoluteSize();
|
||||
auto parent_abs_pos = this->GetParent()->GetAbsolutePosition();
|
||||
Vector2 Widget::GetAbsolutePaddingTopLeft() const {
|
||||
auto parent_abs_size = this->GetParent()->GetAbsoluteSize();
|
||||
UDim2 pad_topleft = {parent->PaddingLeft(), parent->PaddingTop()};
|
||||
return ComputeElementPadding(parent_abs_size, pad_topleft);
|
||||
}
|
||||
|
||||
auto abs_size = GetAbsoluteSize();
|
||||
Vector2 Widget::GetAbsoluteMarginTopLeft() {
|
||||
// TODO: Implement correctly.
|
||||
return {0,0};
|
||||
}
|
||||
|
||||
auto anchor_offset = abs_size * AnchorPoint();
|
||||
Vector2 Widget::GetAbsolutePaddingBottomRight() const {
|
||||
// Returns the amount by which the widget size should be shrunk.
|
||||
// This combines the Left + Right for horizontal,
|
||||
// and Top + Bottom for vertical padding.
|
||||
|
||||
UDim padding_h = parent->PaddingLeft();
|
||||
UDim padding_v = parent->PaddingTop();
|
||||
UDim padding_h = parent->PaddingLeft() + parent->PaddingRight();
|
||||
UDim padding_v = parent->PaddingTop() + parent->PaddingBottom();
|
||||
UDim2 pad_bottom_left = {padding_h, padding_v};
|
||||
|
||||
float calculated_padding_x = padding_h.Pixels + (padding_h.Scale * parent_abs_size.x);
|
||||
float calculated_padding_y = padding_v.Pixels + (padding_v.Scale * parent_abs_size.y);
|
||||
return ComputeElementPadding(parent->GetAbsoluteSize(), pad_bottom_left);
|
||||
}
|
||||
|
||||
Vector2 padding_offset = {calculated_padding_x, calculated_padding_y};
|
||||
Vector2 Widget::GetAbsoluteMarginBottomRight() {
|
||||
// TODO: Implement correctly.
|
||||
return {0,0};
|
||||
}
|
||||
|
||||
|
||||
Vector2 absolute_position =
|
||||
Vector2 Widget::GetAbsolutePosition() const {
|
||||
|
||||
if (this->parent == nullptr)
|
||||
return {0,0};
|
||||
|
||||
auto child_pos_scale = this->Position().GetScale();
|
||||
auto child_pos_pixels = this->Position().GetPixels();
|
||||
auto parent_abs_size = this->GetParent()->GetAbsoluteSize();
|
||||
auto parent_abs_pos = this->GetParent()->GetAbsolutePosition();
|
||||
|
||||
auto abs_size = GetAbsoluteSize();
|
||||
|
||||
auto anchor_offset = abs_size * AnchorPoint();
|
||||
|
||||
Vector2 padding_offset = GetAbsolutePaddingTopLeft();
|
||||
|
||||
Vector2 absolute_position =
|
||||
parent_abs_pos + child_pos_pixels + (parent_abs_size * child_pos_scale) + padding_offset;
|
||||
|
||||
return absolute_position - anchor_offset;
|
||||
}
|
||||
|
||||
// TODO: implement padding shrinking abs_size
|
||||
Vector2 Widget::GetAbsoluteSize() const {
|
||||
Vector2 child_size_scale = this->Size().GetScale();
|
||||
Vector2 child_size_px = this->Size().GetPixels();
|
||||
|
||||
Vector2 parent_abs_size = this->GetParent()->GetAbsoluteSize();
|
||||
|
||||
UDim padding_h = parent->PaddingLeft() + parent->PaddingRight();
|
||||
float final_pad_x = padding_h.Pixels + (padding_h.Scale * parent->GetAbsoluteSize().x);
|
||||
|
||||
UDim padding_v = parent->PaddingTop() + parent->PaddingBottom();
|
||||
float final_pad_y = padding_v.Pixels + (padding_v.Scale * parent->GetAbsoluteSize().y);
|
||||
|
||||
Vector2 pad_size_reduction = {final_pad_x, final_pad_y};
|
||||
|
||||
Vector2 abs_size = child_size_px + (parent_abs_size * child_size_scale) - pad_size_reduction;
|
||||
|
||||
// TODO: Take into account constraints on widgets.
|
||||
return abs_size;
|
||||
return absolute_position - anchor_offset;
|
||||
}
|
||||
|
||||
Vector2 Widget::GetAbsoluteSize() const {
|
||||
|
||||
if (this->GetParent() == nullptr)
|
||||
return {0,0};
|
||||
|
||||
Vector2 child_size_scale = this->Size().GetScale();
|
||||
Vector2 child_size_px = this->Size().GetPixels();
|
||||
|
||||
Vector2 parent_abs_size = this->GetParent()->GetAbsoluteSize();
|
||||
|
||||
Vector2 pad_size_reduction = GetAbsolutePaddingBottomRight();
|
||||
|
||||
Vector2 abs_size = child_size_px + (parent_abs_size * child_size_scale) - pad_size_reduction;
|
||||
|
||||
return abs_size;
|
||||
}
|
||||
|
||||
|
||||
std::optional<Widget*> Widget::FindFirstChild(const std::string& search_name) {
|
||||
for (auto& child : children) {
|
||||
if (child->Name() == search_name)
|
||||
return child;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
std::optional<Widget*> Widget::FindFirstChild(const std::string& search_name) {
|
||||
for (auto& child : children) {
|
||||
if (child->Name() == search_name)
|
||||
return child;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
void Widget::Draw() {
|
||||
if (!visible)
|
||||
return;
|
||||
|
||||
PreDraw();
|
||||
InnerDraw();
|
||||
PostDraw();
|
||||
DrawChildWidgets();
|
||||
}
|
||||
|
||||
void Widget::Draw() {
|
||||
if (!visible)
|
||||
return;
|
||||
void Widget::UpdateTweens(float elapsed) {
|
||||
for (Tween* t: tweens)
|
||||
t->Update(elapsed);
|
||||
|
||||
DrawChildWidgets();
|
||||
}
|
||||
// TODO: Remove tweens if "dead"
|
||||
|
||||
void Widget::Update(float delta) {
|
||||
|
||||
UpdateChildWidgets(delta);
|
||||
}
|
||||
|
||||
void Widget::DrawChildWidgets() {
|
||||
for (auto child : children) {
|
||||
child->Draw();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::UpdateChildWidgets(float delta) {
|
||||
for (auto child : children) {
|
||||
child->Update(delta);
|
||||
}
|
||||
}
|
||||
|
||||
Widget* Widget::GetFamilyTreeRoot() const {
|
||||
// This is stupid, Fix ASAP!
|
||||
auto parent = this->GetParent();
|
||||
if (parent->GetParent() == nullptr)
|
||||
return parent;
|
||||
return parent->GetFamilyTreeRoot();
|
||||
}
|
||||
|
||||
bool Widget::IsMouseInside() const {
|
||||
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) {
|
||||
tweens.erase(std::remove_if(tweens.begin(), tweens.end(), [](Tween* t){
|
||||
if (t->HasCompleted())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}), tweens.end());
|
||||
}
|
||||
|
||||
void Widget::Update(float delta) {
|
||||
UpdateChildWidgets(delta);
|
||||
UpdateTweens(delta);
|
||||
}
|
||||
|
||||
struct {
|
||||
bool operator()(Widget* a, Widget* b) const { return a->ZIndex() < b->ZIndex();}
|
||||
}
|
||||
|
||||
zIndexSort;
|
||||
|
||||
void Widget::DrawChildWidgets() {
|
||||
std::sort(children.begin(), children.end(), zIndexSort);
|
||||
for (auto child : children)
|
||||
child->Draw();
|
||||
}
|
||||
|
||||
void Widget::UpdateChildWidgets(float delta) {
|
||||
for (auto child : children)
|
||||
child->Update(delta);
|
||||
}
|
||||
|
||||
Widget* Widget::GetFamilyTreeRoot() const {
|
||||
// This is stupid, Fix ASAP!
|
||||
auto parent = this->GetParent();
|
||||
if (parent->GetParent() == nullptr)
|
||||
return parent;
|
||||
return parent->GetFamilyTreeRoot();
|
||||
}
|
||||
|
||||
bool Widget::IsMouseInside() const {
|
||||
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::ObserveMouseInput(MouseButton btn, bool pressed) {
|
||||
mbtn = btn;
|
||||
mb_state = pressed;
|
||||
|
||||
for (Widget* child : children) {
|
||||
if (child->ObserveMouseInput(btn, pressed))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Widget::ObserveMouseInput(MouseButton btn, bool pressed) {
|
||||
mbtn = btn;
|
||||
mb_state = pressed;
|
||||
bool Widget::ObserveMouseMovement(const Vector2 &latest_known_pos) {
|
||||
last_known_mouse_pos = latest_known_pos;
|
||||
|
||||
for (Widget* child : children)
|
||||
{
|
||||
child->ObserveMouseInput(btn, pressed);
|
||||
}
|
||||
for (Widget* child: children)
|
||||
if (child->ObserveMouseMovement(latest_known_pos))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Widget::ObserveKeyInput(Key key, bool pressed) {
|
||||
for (Widget* child : children)
|
||||
if (child->ObserveKeyInput(key, pressed))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Widget::ObserveMouseWheel(int mwheel) {
|
||||
// TODO: Log that this is a stub.
|
||||
for (auto& child : children) {
|
||||
if (child->ObserveMouseWheel(mwheel))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Widget::ObserveMouseMovement(const Vector2 &latest_known_pos) {
|
||||
|
||||
last_known_mouse_pos = latest_known_pos;
|
||||
|
||||
for (Widget* child : children)
|
||||
{
|
||||
child->ObserveMouseMovement(latest_known_pos);
|
||||
}
|
||||
}
|
||||
|
||||
bool Widget::IsAncestorOf(Widget *descendant) const {
|
||||
if (descendant == nullptr)
|
||||
return false;
|
||||
Widget *instance = descendant;
|
||||
while (instance->GetParent()) {
|
||||
instance = instance->GetParent();
|
||||
if (instance == this)
|
||||
return true;
|
||||
}
|
||||
bool Widget::IsAncestorOf(Widget *descendant) const {
|
||||
if (descendant == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Widget::IsDescendantOf(Widget *ancestor) const {
|
||||
if (ancestor == nullptr)
|
||||
return false;
|
||||
const Widget *instance = this;
|
||||
while (instance->GetParent()) {
|
||||
instance = instance->GetParent();
|
||||
if (instance == ancestor)
|
||||
return true;
|
||||
}
|
||||
Widget* instance = descendant;
|
||||
while (instance->GetParent()) {
|
||||
instance = instance->GetParent();
|
||||
if (instance == this)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Widget::IsDescendantOf(Widget *ancestor) const {
|
||||
if (ancestor == nullptr)
|
||||
return false;
|
||||
const Widget *instance = this;
|
||||
while (instance->GetParent()) {
|
||||
instance = instance->GetParent();
|
||||
if (instance == ancestor)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Widget *Widget::Add(Widget *newChild) {
|
||||
newChild->Parent(this);
|
||||
return newChild;
|
||||
}
|
||||
Widget *Widget::Add(Widget *newChild) {
|
||||
newChild->Parent(this);
|
||||
return newChild;
|
||||
}
|
||||
|
||||
void Widget::AnchorPoint(const Vector2& point) {
|
||||
anchor_point = point;
|
||||
}
|
||||
void Widget::AnchorPoint(const Vector2& point) {
|
||||
anchor_point = point;
|
||||
}
|
||||
|
||||
// TODO: Implement Z-Index sorting in render routine.
|
||||
int Widget::ZIndex() const { return zindex;}
|
||||
int Widget::ZIndex() const { return zindex;}
|
||||
|
||||
void Widget::ZIndex(int new_zindex) { zindex = new_zindex; }
|
||||
void Widget::ZIndex(int new_zindex) { zindex = new_zindex; }
|
||||
|
||||
AABB2D Widget::GetActualRenderBounds() const {
|
||||
return {GetAbsolutePosition(), GetAbsoluteSize()};
|
||||
}
|
||||
|
||||
void Widget::SetViewportSize(const Vector2 &vps) {
|
||||
viewport_size = vps;
|
||||
|
||||
for(auto& child : children)
|
||||
child->SetViewportSize(viewport_size);
|
||||
}
|
||||
|
||||
Vector2 Widget::ComputeElementPadding(const Vector2 &size, const UDim2 &padding) const {
|
||||
float pad_x = padding.X.Pixels + (padding.X.Scale * size.x);
|
||||
float pad_y = padding.Y.Pixels + (padding.Y.Scale * size.y);
|
||||
|
||||
return {pad_x, pad_y};
|
||||
}
|
||||
|
||||
AABB2D Widget::AbsoluteBounds() const {
|
||||
return {GetAbsolutePosition(), GetAbsoluteSize()};
|
||||
}
|
||||
|
||||
Tween* Widget::TweenPosition(const UDim2& goal, TweenInfo params) {
|
||||
UDim2 start_position = this->Position();
|
||||
TweenTickFunc updateTillGoalReached = [this, start_position, goal] (float elapsed, float progress) mutable {
|
||||
UDim2 pos = start_position.Lerp(goal, progress);
|
||||
Position(pos);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, params);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween* Widget::TweenSize(const UDim2& goal, TweenInfo params) {
|
||||
UDim2 start_size = this->Size();
|
||||
TweenTickFunc updateTillGoalReached = [this, start_size, goal] (float elapsed, float progress) mutable {
|
||||
UDim2 step_size = start_size.Lerp(goal, progress);
|
||||
Size(step_size);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, params);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween* Widget::TweenPaddingLeft(const JUI::UDim& goal, JUI::TweenInfo info) {
|
||||
//UDum2
|
||||
UDim start_padding = this->PaddingLeft();
|
||||
TweenTickFunc updateTillGoalReached = [this, start_padding, goal] (float elapsed, float progress) mutable {
|
||||
UDim step_padding = start_padding.Lerp(goal, progress);
|
||||
PaddingLeft(step_padding);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween* Widget::TweenPaddingRight(const JUI::UDim& goal, JUI::TweenInfo info) {
|
||||
//UDum2
|
||||
UDim start_padding = this->PaddingRight();
|
||||
TweenTickFunc updateTillGoalReached = [this, start_padding, goal] (float elapsed, float progress) mutable {
|
||||
UDim step_padding = start_padding.Lerp(goal, progress);
|
||||
PaddingRight(step_padding);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween* Widget::TweenPaddingTop(const JUI::UDim& goal, JUI::TweenInfo info) {
|
||||
//UDum2
|
||||
UDim start_padding = this->PaddingTop();
|
||||
TweenTickFunc updateTillGoalReached = [this, start_padding, goal] (float elapsed, float progress) mutable {
|
||||
UDim step_padding = start_padding.Lerp(goal, progress);
|
||||
PaddingTop(step_padding);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween* Widget::TweenPaddingBottom(const JUI::UDim& goal, JUI::TweenInfo info) {
|
||||
//UDum2
|
||||
UDim start_padding = this->PaddingBottom();
|
||||
TweenTickFunc updateTillGoalReached = [this, start_padding, goal] (float elapsed, float progress) mutable {
|
||||
UDim step_padding = start_padding.Lerp(goal, progress);
|
||||
PaddingBottom(step_padding);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween *Widget::TweenPadding(const JUI::UDim &gl, const JUI::UDim >, const JUI::UDim &gr,
|
||||
const JUI::UDim &gb, JUI::TweenInfo info) {
|
||||
UDim spl = this->PaddingLeft();
|
||||
UDim spr = this->PaddingRight();
|
||||
UDim spt = this->PaddingTop();
|
||||
UDim spb = this->PaddingBottom();
|
||||
|
||||
TweenTickFunc updateTillGoalReached = [this, spl, spr, spt, spb, gl, gt, gr, gb] (float elapsed, float progress) mutable {
|
||||
UDim step_l = spl.Lerp(gl, progress);
|
||||
UDim step_r = spr.Lerp(gr, progress);
|
||||
UDim step_b = spb.Lerp(gb, progress);
|
||||
UDim step_t = spt.Lerp(gt, progress);
|
||||
Padding(step_l, step_r, step_b, step_t);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween *Widget::TweenPadding(const JUI::UDim &goal, JUI::TweenInfo info) {
|
||||
UDim start_padding = this->PaddingTop(); // TODO: Determine which side is most appropriate as start_point.
|
||||
TweenTickFunc updateTillGoalReached = [this, start_padding, goal] (float elapsed, float progress) mutable {
|
||||
UDim step_padding = start_padding.Lerp(goal, progress);
|
||||
Padding(step_padding);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween *Widget::TweenMargin(const JUI::UDim &gl, const JUI::UDim >, const JUI::UDim &gr,
|
||||
const JUI::UDim &gb, JUI::TweenInfo info) {
|
||||
UDim spl = this->MarginLeft();
|
||||
UDim spr = this->MarginRight();
|
||||
UDim spt = this->MarginTop();
|
||||
UDim spb = this->MarginBottom();
|
||||
|
||||
TweenTickFunc updateTillGoalReached = [this, spl, spr, spt, spb, gl, gt, gr, gb] (float elapsed, float progress) mutable {
|
||||
UDim step_l = spl.Lerp(gl, progress);
|
||||
UDim step_r = spr.Lerp(gr, progress);
|
||||
UDim step_b = spb.Lerp(gb, progress);
|
||||
UDim step_t = spt.Lerp(gt, progress);
|
||||
Margin(step_l, step_r, step_b, step_t);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween *Widget::TweenMargin(const JUI::UDim &goal, JUI::TweenInfo info) {
|
||||
UDim start_Margin = this->MarginTop(); // TODO: Determine which side is most appropriate as start_point.
|
||||
TweenTickFunc updateTillGoalReached = [this, start_Margin, goal] (float elapsed, float progress) mutable {
|
||||
UDim step_Margin = start_Margin.Lerp(goal, progress);
|
||||
Margin(step_Margin);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween* Widget::TweenMarginLeft(const JUI::UDim& goal, JUI::TweenInfo info) {
|
||||
UDim start = this->MarginLeft();
|
||||
TweenTickFunc updateTillGoalReached = [this, start, goal] (float elapsed, float progress) mutable {
|
||||
UDim step = start.Lerp(goal, progress);
|
||||
MarginLeft(step);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween* Widget::TweenMarginRight(const JUI::UDim& goal, JUI::TweenInfo info) {
|
||||
UDim start = this->MarginRight();
|
||||
TweenTickFunc updateTillGoalReached = [this, start, goal] (float elapsed, float progress) mutable {
|
||||
UDim step = start.Lerp(goal, progress);
|
||||
MarginRight(step);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween* Widget::TweenMarginTop(const JUI::UDim& goal, JUI::TweenInfo info) {
|
||||
UDim start = this->MarginTop();
|
||||
TweenTickFunc updateTillGoalReached = [this, start, goal] (float elapsed, float progress) mutable {
|
||||
UDim step = start.Lerp(goal, progress);
|
||||
MarginTop(step);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween* Widget::TweenMarginBottom(const JUI::UDim& goal, JUI::TweenInfo info) {
|
||||
UDim start = this->MarginBottom();
|
||||
TweenTickFunc updateTillGoalReached = [this, start, goal] (float elapsed, float progress) mutable {
|
||||
UDim step = start.Lerp(goal, progress);
|
||||
MarginBottom(step);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
Tween *Widget::TweenAnchorPoint(const Vector2 &goal, TweenInfo info) {
|
||||
Vector2 start = this->AnchorPoint();
|
||||
|
||||
TweenTickFunc updateTillGoalReached = [this, start, goal] (float elapsed, float progress) mutable {
|
||||
Vector2 step = start.Lerp(goal, progress);
|
||||
|
||||
AnchorPoint(step);
|
||||
};
|
||||
|
||||
Tween* t = new Tween(updateTillGoalReached, info);
|
||||
tweens.push_back(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
|
||||
float Widget::ComputeElementPadding(float size, const UDim &padding) {
|
||||
return padding.Pixels + (padding.Scale * size);
|
||||
}
|
||||
|
||||
AABB2D Widget::GetActualRenderBounds() const {
|
||||
return {GetAbsolutePosition(), GetAbsoluteSize()};
|
||||
}
|
||||
|
||||
}
|
4
src/JUI/DefaultStyle.cpp
Normal file
4
src/JUI/DefaultStyle.cpp
Normal file
@@ -0,0 +1,4 @@
|
||||
#include <JUI/DefaultStyle.hpp>
|
||||
|
||||
namespace DefaultStyle
|
||||
{ }
|
@@ -2,8 +2,45 @@
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
// TODO: This is duplicated here and in ReCaveGame::Loggers.cpp
|
||||
// Bring into jlog as utility functions.
|
||||
std::string format_timestamp(jlog::Timestamp ts) {
|
||||
return std::format(
|
||||
"{}-{}-{} {}:{}:{}.{}",
|
||||
ts.Year(),
|
||||
ts.Month(),
|
||||
ts.Day(),
|
||||
ts.Hour().count(),
|
||||
ts.Minute().count(),
|
||||
ts.Second().count(),
|
||||
ts.Millisecond().count());
|
||||
}
|
||||
|
||||
// TODO: This is duplicated here and in ReCaveGame::Loggers.cpp
|
||||
// Bring into jlog as utility functions.
|
||||
std::string format_source_location(const std::source_location& location) {
|
||||
return std::format("{} @ {}:{}", location.function_name(), location.file_name(), location.line());
|
||||
}
|
||||
|
||||
JUILogger::JUILogger(const std::string &context, std::ofstream &file, Color4 contextColor, Color4 timestampColor,
|
||||
Color4 locationColor, Color4 pointerColor, Color4 messageColor)
|
||||
: jlog::GenericLogger(context, file, contextColor, timestampColor, locationColor, pointerColor, messageColor) { }
|
||||
|
||||
void JUILogger::Log(const std::string &message, const std::source_location &location, const jlog::Timestamp &ts) {
|
||||
GenericLogger::Log(message, location, ts);
|
||||
|
||||
std::string formatted_timestamp = format_timestamp(ts);
|
||||
std::string formatted_source_location = format_source_location(location);
|
||||
|
||||
std::string final_result = std::format("[{}] [{}] []", formatted_timestamp, this->ConsoleLogger::context, message);
|
||||
|
||||
OnLog.Invoke(final_result, messageColor);
|
||||
}
|
||||
|
||||
std::ofstream GlobalLogFile("latest.log", std::ios_base::app);
|
||||
jlog::GenericLogger UILogs {"JUI", GlobalLogFile, Colors::Green, Colors::Gray, Colors::Gray, Colors::Green, Colors::White};
|
||||
JUILogger UILogs {"JUI", GlobalLogFile, Colors::Green, Colors::Gray, Colors::Gray, Colors::Green, Colors::White};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@@ -6,9 +6,23 @@ void JUI::Hoverable::OnHover(const Vector2 &MousePos) {
|
||||
|
||||
void JUI::Hoverable::OnExit(const Vector2 &MousePos) {
|
||||
OnExitEvent.Invoke(MousePos);
|
||||
|
||||
//tooltip_threshold = 0;
|
||||
}
|
||||
|
||||
void JUI::Hoverable::Update(const Vector2 &m_pos, float delta) {
|
||||
|
||||
/*if (tooltip != nullptr)
|
||||
{
|
||||
if (IsHovered()) {
|
||||
tooltip->Visible(true);
|
||||
tooltip->Position(UDim2::FromPixels(m_pos.x, m_pos.y));
|
||||
} else {
|
||||
tooltip->Visible(false);
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
if (IsHovered() && !hover_debounce) {
|
||||
OnHover(m_pos);
|
||||
hover_debounce = true;
|
||||
@@ -19,3 +33,5 @@ void JUI::Hoverable::Update(const Vector2 &m_pos, float delta) {
|
||||
hover_debounce = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
202
src/JUI/Tween.cpp
Normal file
202
src/JUI/Tween.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
#include <JUI/Tween.hpp>
|
||||
#include <J3ML/J3ML.hpp>
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
using namespace J3ML;
|
||||
|
||||
float EasingFunctions::EaseInSine(float t) { return Math::Sin(Math::PiOverTwo * t); }
|
||||
|
||||
float EasingFunctions::EaseOutSine(float t) { return 1 + Math::Sin(Math::PiOverTwo * (--t)); }
|
||||
|
||||
float EasingFunctions::EaseInOutSine(float t) { return 0.5f * (1 + Math::Sin(Math::Pi * (t - 0.5f))); }
|
||||
|
||||
float EasingFunctions::EaseInQuad(float t) { return t * t;}
|
||||
|
||||
float EasingFunctions::EaseOutQuad(float t) { return t * (2.f - t);}
|
||||
|
||||
float EasingFunctions::EaseInOutQuad(float t) { return t < 0.5f ? 2.f * t * t : t * (4.f - 2.f * t) - 1; }
|
||||
|
||||
float EasingFunctions::EaseInCubic(float t) { return t * t * t;}
|
||||
|
||||
float EasingFunctions::EaseOutCubic(float t) { return 1.f + (--t) * t * t; }
|
||||
|
||||
float EasingFunctions::EaseInOutCubic(float t) { return t < 0.5f ? 4.f * t * t * t : 1 + (--t) * (2 * (--t)) * (2 * t); }
|
||||
|
||||
float EasingFunctions::EaseInQuart(float t) {
|
||||
t *= t;
|
||||
return t * t;
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseOutQuart(float t) {
|
||||
t = (--t) * t;
|
||||
return 1.f - t * t;
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInOutQuart(float t) {
|
||||
if (t < 0.5f) {
|
||||
t *= t;
|
||||
return 8 * t * t;
|
||||
} else {
|
||||
t = (--t) * t;
|
||||
return 1 - 8 * t * t;
|
||||
}
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInQuint(float t) {
|
||||
float t2 = t * t;
|
||||
return t * t2 * t2;
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseOutQuint(float t) {
|
||||
float t2 = (--t) * t;
|
||||
return 1 + t * t2 * t2;
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInOutQuint(float t) {
|
||||
float t2;
|
||||
if ( t < 0.5f) {
|
||||
t2 = t * t;
|
||||
return 16 * t * t2 * t2;
|
||||
} else {
|
||||
t2 = (--t) * t;
|
||||
return 1 + 16 * t * t2 * t2;
|
||||
}
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInExpo(float t) {
|
||||
return (Math::Pow(2, 8*t) - 1) / 255;
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseOutExpo(float t) {
|
||||
return 1 - Math::Pow(2, -8*t);
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInOutExpo(float t) {
|
||||
if (t < 0.5f) {
|
||||
return (Math::Pow(2, 16 * t) - 1) / 510;
|
||||
} else {
|
||||
return 1 - 0.5f * Math::Pow(2, -16 * (t - 0.5f));
|
||||
}
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInCirc(float t) {
|
||||
return 1 - Math::Sqrt(1 - t);
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseOutCirc(float t) {
|
||||
return Math::Sqrt(t);
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInOutCirc(float t) {
|
||||
if (t < 0.5f) {
|
||||
return (1 - Math::Sqrt(1 - 2 * t)) * 0.5f;
|
||||
} else {
|
||||
return (1 + Math::Sqrt(2 * t - 1)) * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInBack(float t) { return t * t * (2.70158f * t - 1.70158f); }
|
||||
|
||||
float EasingFunctions::EaseOutBack(float t) { return 1 + (--t) * t * (2.70158 * t + 1.70158); }
|
||||
|
||||
float EasingFunctions::EaseInOutBack(float t) {
|
||||
if (t < 0.5f) {
|
||||
return t * t * (7 * t - 2.5f) * 2.f;
|
||||
} else {
|
||||
return 1 + (--t) * t * 2 * (7 * t + 2.5f);
|
||||
}
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInElastic(float t) {
|
||||
float t2 = t * t;
|
||||
return t2 * t2 * Math::Sin(t * Math::Pi * 4.5f);
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseOutElastic(float t) {
|
||||
float t2 = (t - 1) * (t - 1);
|
||||
return 1 - t2 * t2 * Math::Cos(t * Math::Pi * 4.5f);
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInOutElastic(float t) {
|
||||
float t2;
|
||||
if (t < 0.45f) {
|
||||
t2 = t * t;
|
||||
return 8 * t2 * t2 * Math::Sin(t * Math::Pi * 9);
|
||||
} else if (t < 0.55f) {
|
||||
return 0.5f + 0.75f * Math::Sin(t * Math::Pi * 4);
|
||||
} else {
|
||||
t2 = (t - 1) * (t - 1);
|
||||
return 1 - 8 * t2 * t2 * Math::Sin(t * Math::Pi * 9);
|
||||
}
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInBounce(float t) {
|
||||
return Math::Pow(2, 6 * (t - 1)) * Math::Abs(Math::Sin(t * Math::Pi * 3.5f));
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseOutBounce(float t) {
|
||||
return 1 - Math::Pow(2, -6 * t ) * Math::Abs(Math::Cos(t * Math::Pi * 3.5f));
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInOutBounce(float t) {
|
||||
if (t < 0.5f) {
|
||||
return 8 * Math::Pow(2, 8 * (t - 1)) * Math::Abs(Math::Sin(t * Math::Pi));
|
||||
} else {
|
||||
return 1 - 8 * Math::Pow(2, -8 * t) * Math::Abs(Math::Sin(t * Math::Pi * 7));
|
||||
}
|
||||
}
|
||||
|
||||
float EasingFunctions::EaseInOutLinear(float t) { return t;}
|
||||
|
||||
void Tween::Update(float elapsed) {
|
||||
|
||||
if (time <= 0)
|
||||
{
|
||||
// TODO: Error in this case.
|
||||
completed = true;
|
||||
}
|
||||
|
||||
|
||||
if (delay_time > 0)
|
||||
{
|
||||
delay_time -= elapsed;
|
||||
return;
|
||||
}
|
||||
|
||||
progress += (elapsed / time);
|
||||
|
||||
if (progress >= 1.f)
|
||||
{
|
||||
progress = 1.f;
|
||||
if (!completed)
|
||||
{
|
||||
completed = true;
|
||||
Completed.Invoke();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float modified_progress = easing_func(progress);
|
||||
|
||||
tick_func(elapsed, modified_progress);
|
||||
}
|
||||
|
||||
void Tween::ForceFinish() {
|
||||
progress = 1.f;
|
||||
}
|
||||
|
||||
void Tween::Cancel() {
|
||||
completed = true;
|
||||
}
|
||||
|
||||
bool Tween::Paused() const { return paused; }
|
||||
|
||||
bool Tween::HasCompleted() const { return completed; }
|
||||
|
||||
void Tween::Start() { progress = 0; Resume(); }
|
||||
|
||||
Tween::Tween(TweenTickFunc tick_func) {
|
||||
this->tick_func = tick_func;
|
||||
}
|
||||
}
|
@@ -31,3 +31,16 @@ JUI::UDim JUI::UDim::operator*(float rhs) const {
|
||||
JUI::UDim JUI::UDim::operator/(float rhs) const {
|
||||
return {static_cast<int>(Pixels / rhs), Scale/rhs};
|
||||
}
|
||||
|
||||
JUI::UDim JUI::UDim::Lerp(const JUI::UDim &goal, float t) {
|
||||
float scale = J3ML::Math::Lerp(Scale, goal.Scale, t);
|
||||
float pixels = J3ML::Math::Lerp(Pixels, goal.Pixels, t);
|
||||
|
||||
return UDim(pixels, scale);
|
||||
}
|
||||
|
||||
bool JUI::UDim::Equals(const JUI::UDim &rhs, float epsilon) {
|
||||
return J3ML::Math::Equal((float)Pixels, rhs.Pixels, epsilon)
|
||||
&& J3ML::Math::Equal(Scale, rhs.Scale, epsilon);
|
||||
}
|
||||
|
||||
|
@@ -47,3 +47,15 @@ Vector2 JUI::UDim2::GetScale() const {
|
||||
Vector2 JUI::UDim2::GetPixels() const {
|
||||
return {static_cast<float>(X.Pixels), static_cast<float>(Y.Pixels)};
|
||||
}
|
||||
|
||||
JUI::UDim2 JUI::UDim2::Lerp(const JUI::UDim2 &goal, float t) {
|
||||
return UDim2(
|
||||
X.Lerp(goal.X, t),
|
||||
Y.Lerp(goal.Y, t)
|
||||
);
|
||||
}
|
||||
|
||||
bool JUI::UDim2::Equals(const JUI::UDim2 &rhs, float epsilon) {
|
||||
return X.Equals(rhs.X, epsilon) &&
|
||||
Y.Equals(rhs.Y, epsilon);
|
||||
}
|
||||
|
@@ -2,65 +2,22 @@
|
||||
#include <JUI/Widgets/Button.hpp>
|
||||
#include <jlog/Logger.hpp>
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
Button::Button(): Widget() {}
|
||||
namespace JUI {
|
||||
Button::Button(): Rect(), Clickable() {
|
||||
Name("Button");
|
||||
BGColor(BaseBGColor());
|
||||
BorderColor(BaseBorderColor());
|
||||
}
|
||||
|
||||
Button::Button(Widget *parent): Clickable() {
|
||||
Button::Button(Widget *parent): Button() {
|
||||
this->Parent(parent);
|
||||
};
|
||||
|
||||
void Button::Draw() {
|
||||
if (!visible)
|
||||
return;
|
||||
|
||||
J2D::Begin();
|
||||
Vector2 abs_pos = GetAbsolutePosition();
|
||||
Vector2 abs_size = GetAbsoluteSize();
|
||||
|
||||
auto root_size = GetFamilyTreeRoot()->GetAbsoluteSize();
|
||||
|
||||
GLint *old_scissor_bounds;
|
||||
bool clip_was_enabled;
|
||||
|
||||
if (clips_descendants) {
|
||||
clip_was_enabled = glIsEnabled(GL_SCISSOR_TEST);
|
||||
if (clip_was_enabled)
|
||||
glGetIntegerv(GL_SCISSOR_BOX, old_scissor_bounds);
|
||||
|
||||
float presumed_screen_height = 600;
|
||||
glScissor(abs_pos.x, presumed_screen_height-abs_size.y-abs_pos.y, abs_size.x, abs_size.y);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
RectBase::Draw(abs_pos, abs_size);
|
||||
|
||||
// Draw Child Elements with scissor clipping still active
|
||||
Widget::Draw();
|
||||
|
||||
// Returns clip to previous state
|
||||
if (clips_descendants)
|
||||
{
|
||||
//glScissor(old_scissor_bounds[0], old_scissor_bounds[1], old_scissor_bounds[2], old_scissor_bounds[3]);
|
||||
|
||||
if (!clip_was_enabled) {}
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
}
|
||||
|
||||
J2D::End();
|
||||
}
|
||||
|
||||
|
||||
void Button::OnClick(const Vector2& mouse_pos, const MouseButton& btn)
|
||||
{
|
||||
void Button::OnClick(const Vector2& mouse_pos, const MouseButton& btn) {
|
||||
if (disabled)
|
||||
return;
|
||||
|
||||
Clickable::OnClick(mouse_pos, btn);
|
||||
|
||||
BGColor(PressedBGColor());
|
||||
BorderColor(PressedBorderColor());
|
||||
}
|
||||
|
||||
void Button::OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &btn,
|
||||
@@ -70,9 +27,22 @@ namespace JUI
|
||||
return;
|
||||
|
||||
Clickable::OnRelease(mouse_pos, btn, still_hovering);
|
||||
}
|
||||
|
||||
BGColor(BaseBGColor());
|
||||
BorderColor(BaseBorderColor());
|
||||
void Button::UpdateVisualState() {
|
||||
if (Disabled()) {
|
||||
BGColor(DisabledBGColor());
|
||||
BorderColor(DisabledBorderColor());
|
||||
} else if (IsClicked()) {
|
||||
BGColor(PressedBGColor());
|
||||
BorderColor(PressedBorderColor());
|
||||
} else if (IsHovered()) {
|
||||
BGColor(HoveredBGColor());
|
||||
BorderColor(HoveredBorderColor());
|
||||
} else {
|
||||
BGColor(BaseBGColor());
|
||||
BorderColor(BaseBorderColor());
|
||||
}
|
||||
}
|
||||
|
||||
void Button::Update(float delta) {
|
||||
@@ -92,7 +62,11 @@ namespace JUI
|
||||
OnRelease(last_known_mouse_pos, mbtn, IsHovered());
|
||||
}
|
||||
|
||||
UpdateVisualState();
|
||||
|
||||
prev_mb_state = mb_state;
|
||||
|
||||
Rect::Update(delta);
|
||||
}
|
||||
|
||||
void Button::Enable() {
|
||||
@@ -134,8 +108,6 @@ namespace JUI
|
||||
if (Disabled() || IsClicked())
|
||||
return;
|
||||
|
||||
BGColor(HoveredBGColor());
|
||||
BorderColor(HoveredBorderColor());
|
||||
}
|
||||
|
||||
void Button::OnExit(const Vector2 &MousePos) {
|
||||
@@ -143,9 +115,6 @@ namespace JUI
|
||||
|
||||
if (Disabled() || IsClicked())
|
||||
return;
|
||||
|
||||
BGColor(BaseBGColor());
|
||||
BorderColor(BaseBorderColor());
|
||||
}
|
||||
|
||||
Color4 Button::HoveredBGColor() const { return hover_bg; }
|
||||
@@ -180,5 +149,19 @@ namespace JUI
|
||||
|
||||
void Button::DisabledBorderColor(const Color4 &color) { disabled_border = color; }
|
||||
|
||||
void Button::BorderColors(const Color4 &base, const Color4 &hover, const Color4 &pressed, const Color4 &disabled) {
|
||||
BaseBorderColor(base);
|
||||
HoveredBorderColor(hover);
|
||||
PressedBorderColor(pressed);
|
||||
DisabledBGColor(disabled);
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
void Button::BGColors(const Color4 &base, const Color4 &hover, const Color4 &pressed, const Color4 &disabled) {
|
||||
BaseBGColor(base);
|
||||
HoveredBGColor(hover);
|
||||
PressedBGColor(pressed);
|
||||
DisabledBGColor(disabled);\
|
||||
UpdateVisualState();
|
||||
}
|
||||
}
|
@@ -1 +1,33 @@
|
||||
#include <JUI/Widgets/Checkbox.hpp>
|
||||
#include <JUI/Widgets/Checkbox.hpp>
|
||||
|
||||
|
||||
namespace JUI {
|
||||
|
||||
Checkbox::Checkbox() : Button(), CheckboxBase() {
|
||||
Padding(2_px);
|
||||
}
|
||||
|
||||
Checkbox::Checkbox(Widget *parent) : Checkbox() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
void Checkbox::Update(float delta) {
|
||||
Button::Update(delta);
|
||||
}
|
||||
|
||||
void Checkbox::OnRelease(const Vector2 &mouse_pos, const MouseButton &bnt, bool still_hovering) {
|
||||
Button::OnRelease(mouse_pos, bnt, still_hovering);
|
||||
checked = !checked;
|
||||
}
|
||||
|
||||
void Checkbox::InnerDraw() {
|
||||
Rect::InnerDraw();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
84
src/JUI/Widgets/Collapsible.cpp
Normal file
84
src/JUI/Widgets/Collapsible.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <JUI/Widgets/Collapsible.hpp>
|
||||
|
||||
namespace JUI {
|
||||
Collapsible::Collapsible() : Rect() {
|
||||
Name("Collapsible");
|
||||
|
||||
header = new TextButton(this);
|
||||
header->Size({100_percent, header_height});
|
||||
header->SetContent("Collapsible");
|
||||
header->BaseBGColor(Colors::DarkGray);
|
||||
header->BGColor(Colors::DarkGray);
|
||||
header->Center();
|
||||
//header->
|
||||
header->OnClickEvent += [this] (auto a, auto b) {
|
||||
if (Collapsed())
|
||||
Expand();
|
||||
else
|
||||
Collapse();
|
||||
};
|
||||
|
||||
lil_arrow = new TextRect(header);
|
||||
lil_arrow->Size({header_height, header_height});
|
||||
lil_arrow->BGColor(Colors::Transparent);
|
||||
lil_arrow->SetContent("/\\");
|
||||
lil_arrow->BorderWidth(0);
|
||||
|
||||
content_box = new Rect(this);
|
||||
content_box->BGColor(Colors::Transparent);
|
||||
content_box->BorderColor(Colors::Transparent);
|
||||
content_box->BorderWidth(0);
|
||||
content_box->Position({0_px, header_height});
|
||||
content_box->Size({100_percent, 100_percent - header_height});
|
||||
|
||||
}
|
||||
|
||||
void Collapsible::Collapse() {
|
||||
//Size(saved_size);
|
||||
lil_arrow->SetContent("\\/");
|
||||
content_box->Visible(false);
|
||||
if (resize_anim != nullptr && !resize_anim->HasCompleted()) {
|
||||
resize_anim->Cancel();
|
||||
} else {
|
||||
saved_size = Size();
|
||||
}
|
||||
|
||||
resize_anim = TweenSize(UDim2(saved_size.X, header_height), {
|
||||
.time = 0.25f
|
||||
});
|
||||
collapsed = true;
|
||||
}
|
||||
|
||||
void Collapsible::Expand() {
|
||||
lil_arrow->SetContent("/\\");
|
||||
content_box->Visible(true);
|
||||
if (resize_anim != nullptr && !resize_anim->HasCompleted())
|
||||
resize_anim->Cancel();
|
||||
|
||||
|
||||
resize_anim = TweenSize(saved_size, {
|
||||
.time = 0.25f
|
||||
});
|
||||
collapsed = false;
|
||||
}
|
||||
|
||||
bool Collapsible::Collapsed() const { return collapsed; }
|
||||
|
||||
Collapsible::Collapsible(Widget *parent) : Collapsible() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
TextButton *Collapsible::Header() { return header; }
|
||||
|
||||
Rect *Collapsible::ContentBox() {return content_box; }
|
||||
|
||||
UDim Collapsible::HeaderHeight() const { return header_height;}
|
||||
|
||||
void Collapsible::HeaderHeight(const UDim &value) { header_height = value; }
|
||||
|
||||
void Collapsible::Title(const std::string &value) {
|
||||
header->SetContent(value);
|
||||
}
|
||||
|
||||
std::string Collapsible::Title() const { return header->GetContent(); }
|
||||
}
|
89
src/JUI/Widgets/CommandLine.cpp
Normal file
89
src/JUI/Widgets/CommandLine.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include <JUI/Widgets/CommandLine.hpp>
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
// Get current date/time, format is YYYY-MM-DD.HH:mm:ss
|
||||
const std::string currentDateTime() {
|
||||
time_t now = time(0);
|
||||
struct tm tstruct;
|
||||
char buf[80];
|
||||
tstruct = *localtime(&now);
|
||||
// Visit http://en.cppreference.com/w/cpp/chrono/c/strftime
|
||||
// for more information about date/time format
|
||||
strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
CommandLine::CommandLine() {
|
||||
this->SetTitle("Console");
|
||||
|
||||
message_log_box = new JUI::ScrollingRect(this->ViewportInstance());
|
||||
message_log_box->Size(JUI::UDim2(0, -input_form_height, 1, 1));
|
||||
|
||||
message_log_list = new JUI::VerticalListLayout(message_log_box);
|
||||
message_log_list->LayoutOrder(JUI::LayoutOrder::V::BOTTOM);
|
||||
|
||||
input_box = new JUI::TextInputForm(this->ViewportInstance());
|
||||
|
||||
input_box->SetAutoCompleteText(">Type Commands Here");
|
||||
input_box->SetContent("");
|
||||
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->SetAutocompleteTextColor(Colors::Gray);
|
||||
|
||||
input_box->OnReturn += [this] (const std::string& msg) {
|
||||
OnInputBoxSend(msg);
|
||||
Log(std::format("[{}] >{}", currentDateTime(), msg));
|
||||
};
|
||||
}
|
||||
|
||||
CommandLine::CommandLine(Widget *parent): CommandLine() {
|
||||
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;
|
||||
}
|
||||
|
||||
void CommandLine::Log(const std::string &message, const Color4 &color) {
|
||||
const Color4 light {60, 60, 60};
|
||||
const Color4 dark {80, 80, 80};
|
||||
|
||||
auto* entry = new JUI::TextRect(message_log_list);
|
||||
entry->Size({100_percent, UDim(message_height, 0)});
|
||||
entry->SetContent(message);
|
||||
entry->LayoutOrder(index++);
|
||||
entry->BGColor(index%2 == 0 ? light : dark);
|
||||
entry->SetTextColor(color);
|
||||
entry->SetTextSize(14);
|
||||
|
||||
message_log_box->canvas_height += entry->GetAbsoluteSize().y;
|
||||
}
|
||||
|
||||
bool CommandLine::ObserveKeyInput(Key key, bool pressed) {
|
||||
if (!IsVisible())
|
||||
return false;
|
||||
|
||||
return Widget::ObserveKeyInput(key, pressed);
|
||||
}
|
||||
|
||||
void CommandLine::OnInputBoxSend(const std::string &message) {
|
||||
OnInput.Invoke(message);
|
||||
input_box->SetContent("");
|
||||
input_box->SetAutoCompleteText("");
|
||||
}
|
||||
}
|
@@ -2,9 +2,8 @@
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
Image::Image() : Widget(), ImageBase()
|
||||
{
|
||||
|
||||
Image::Image() : Widget(), ImageBase() {
|
||||
Name("Image");
|
||||
}
|
||||
|
||||
Image::Image(Widget* parent) : Image()
|
||||
@@ -31,7 +30,7 @@ namespace JUI
|
||||
if (fit_image_to_parent)
|
||||
{
|
||||
auto old_scale = scale;
|
||||
scale = GetAbsoluteSize() / texture->GetDimensions();
|
||||
scale = GetAbsoluteSize() / Vector2(texture->GetDimensions());
|
||||
ImageBase::Draw(GetAbsolutePosition(), GetAbsoluteSize());
|
||||
scale = old_scale;
|
||||
} else
|
||||
|
59
src/JUI/Widgets/ImageButton.cpp
Normal file
59
src/JUI/Widgets/ImageButton.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include <JUI/Widgets/ImageButton.hpp>
|
||||
|
||||
void JUI::ImageButton::Update(float delta) {
|
||||
Button::Update(delta);
|
||||
UpdateImageVisualState();
|
||||
}
|
||||
|
||||
void JUI::ImageButton::Draw() {
|
||||
Button::Draw();
|
||||
ImageBase::Draw(GetAbsolutePosition()+GetAbsolutePaddingTopLeft(), GetAbsoluteSize()-GetAbsolutePaddingBottomRight());
|
||||
}
|
||||
|
||||
JUI::ImageButton::ImageButton() : ImageBase(), Button() {
|
||||
Name("ImageButton");
|
||||
}
|
||||
|
||||
JUI::ImageButton::ImageButton(JUI::Widget *parent) : ImageButton() { Parent(parent); }
|
||||
|
||||
void JUI::ImageButton::HoveredImageColor(const Color4 &value) {
|
||||
hover_img_color = value;
|
||||
}
|
||||
|
||||
void JUI::ImageButton::BaseImageColor(const Color4 &value) {
|
||||
base_img_color = value;
|
||||
}
|
||||
|
||||
void JUI::ImageButton::PressedImageColor(const Color4 &value) {
|
||||
pressed_img_color = value;
|
||||
}
|
||||
|
||||
void JUI::ImageButton::DisabledImageColor(const Color4 &value) {
|
||||
disabled_img_color = value;
|
||||
}
|
||||
|
||||
void JUI::ImageButton::UpdateImageVisualState() {
|
||||
if (Disabled())
|
||||
ImageBase::Color(DisabledImageColor());
|
||||
else if (IsClicked())
|
||||
ImageBase::Color(PressedImageColor());
|
||||
else if (IsHovered())
|
||||
ImageBase::Color(HoveredImageColor());
|
||||
else
|
||||
ImageBase::Color(BaseImageColor());
|
||||
}
|
||||
|
||||
Color4 JUI::ImageButton::HoveredImageColor() const {return hover_img_color;}
|
||||
|
||||
Color4 JUI::ImageButton::BaseImageColor() const { return base_img_color; }
|
||||
|
||||
Color4 JUI::ImageButton::PressedImageColor() const { return pressed_img_color; }
|
||||
|
||||
Color4 JUI::ImageButton::DisabledImageColor() const { return disabled_img_color; }
|
||||
|
||||
void JUI::ImageButton::ImageColors(const Color4 &hover, const Color4 &base, const Color4 &press, const Color4 &disabled) {
|
||||
HoveredImageColor(hover);
|
||||
BaseImageColor(base);
|
||||
PressedImageColor(press);
|
||||
DisabledImageColor(disabled);
|
||||
}
|
@@ -1,2 +1,18 @@
|
||||
#include <JUI/Widgets/ImageRect.hpp>
|
||||
|
||||
JUI::ImageRect::ImageRect() : Rect(), ImageBase() {
|
||||
Name("ImageRect");
|
||||
}
|
||||
|
||||
JUI::ImageRect::ImageRect(JUI::Widget *parent) : ImageRect() {
|
||||
Parent(parent);
|
||||
}
|
||||
|
||||
void JUI::ImageRect::Update(float delta) {
|
||||
Rect::Update(delta);
|
||||
}
|
||||
|
||||
void JUI::ImageRect::Draw() {
|
||||
Rect::Draw();
|
||||
ImageBase::Draw(GetAbsolutePosition()+GetAbsolutePaddingTopLeft(), GetAbsoluteSize()-GetAbsolutePaddingBottomRight());
|
||||
}
|
||||
|
@@ -2,17 +2,26 @@
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
ListLayout::ListLayout() : LayoutContainer() {}
|
||||
ListLayout::ListLayout() : LayoutContainer() {
|
||||
Name("ListLayout");
|
||||
}
|
||||
ListLayout::ListLayout(Widget* parent) : ListLayout() {}
|
||||
|
||||
VerticalListLayout::VerticalListLayout() : ListLayout() {}
|
||||
VerticalListLayout::VerticalListLayout(Widget* parent) : VerticalListLayout()
|
||||
{
|
||||
VerticalListLayout::VerticalListLayout(Widget* parent) : VerticalListLayout() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
struct {
|
||||
bool operator()(Widget* a, Widget* b) const { return a->LayoutOrder() > b->LayoutOrder();}
|
||||
} layoutOrderSort;
|
||||
|
||||
void VerticalListLayout::ApplyLayout() {
|
||||
|
||||
content_height = 0;
|
||||
|
||||
std::sort(children.begin(), children.end(), layoutOrderSort);
|
||||
|
||||
if (layout == LayoutOrder::V::TOP)
|
||||
{
|
||||
int consumed_height = 0;
|
||||
@@ -21,10 +30,18 @@ namespace JUI
|
||||
// TODO: Implement widget.LayoutOrder property
|
||||
// TODO: Sort children by LayoutOrder
|
||||
consumed_height += pad_top.Pixels;
|
||||
content_height += pad_top.Pixels;
|
||||
|
||||
child_widget->Position({0, consumed_height, 0, 0});
|
||||
|
||||
consumed_height += child_widget->GetAbsoluteSize().y;
|
||||
content_height += child_widget->GetAbsoluteSize().y;
|
||||
|
||||
consumed_height += pad_bottom.Pixels;
|
||||
content_height += pad_bottom.Pixels;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (layout == LayoutOrder::V::BOTTOM)
|
||||
@@ -34,13 +51,20 @@ namespace JUI
|
||||
{
|
||||
// TODO: Implement widget.LayoutOrder property
|
||||
// TODO: Sort children by LayoutOrder
|
||||
|
||||
consumed_height -= pad_top.Pixels;
|
||||
content_height += pad_top.Pixels;
|
||||
|
||||
consumed_height -= child_widget->GetAbsoluteSize().y;
|
||||
content_height += child_widget->GetAbsoluteSize().y;
|
||||
|
||||
child_widget->Position({0, consumed_height, 0, 0});
|
||||
|
||||
|
||||
consumed_height -= pad_bottom.Pixels;
|
||||
content_height += pad_bottom.Pixels;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void VerticalListLayout::LayoutOrder(LayoutOrder::V order) {
|
||||
@@ -49,6 +73,10 @@ namespace JUI
|
||||
|
||||
LayoutOrder::V VerticalListLayout::LayoutOrder() const { return layout;}
|
||||
|
||||
float VerticalListLayout::CurrentContentHeight() const {
|
||||
return content_height;
|
||||
}
|
||||
|
||||
HorizontalListLayout::HorizontalListLayout() : ListLayout() {}
|
||||
|
||||
|
||||
@@ -59,15 +87,25 @@ namespace JUI
|
||||
void HorizontalListLayout::ApplyLayout() {
|
||||
// TODO: Implement widget.LayoutOrder and sort by that number.
|
||||
|
||||
std::sort(children.begin(), children.end(), layoutOrderSort);
|
||||
|
||||
if (layout == LayoutOrder::H::LEFT)
|
||||
{
|
||||
|
||||
int consumed_width = 0;
|
||||
for (auto &child_widget : children)
|
||||
{
|
||||
|
||||
consumed_width += pad_left.Pixels;
|
||||
content_width += pad_left.Pixels;
|
||||
|
||||
child_widget->Position({consumed_width, 0, 0, 0});
|
||||
|
||||
consumed_width += child_widget->GetAbsoluteSize().x;
|
||||
content_width += child_widget->GetAbsoluteSize().x;
|
||||
|
||||
consumed_width += pad_right.Pixels;
|
||||
content_width += pad_right.Pixels;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,9 +115,15 @@ namespace JUI
|
||||
for (auto &child_widget : children)
|
||||
{
|
||||
consumed_width -= pad_left.Pixels;
|
||||
content_width += pad_left.Pixels;
|
||||
|
||||
consumed_width -= child_widget->GetAbsoluteSize().x;
|
||||
content_width += child_widget->GetAbsoluteSize().x;
|
||||
|
||||
child_widget->Position({consumed_width, 0, 0, 0});
|
||||
|
||||
consumed_width -= pad_right.Pixels;
|
||||
content_width += pad_right.Pixels;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,4 +135,6 @@ namespace JUI
|
||||
LayoutOrder::H HorizontalListLayout::LayoutOrder() const {
|
||||
return layout;
|
||||
}
|
||||
|
||||
float HorizontalListLayout::CurrentContentWidth() const { return content_width;}
|
||||
}
|
108
src/JUI/Widgets/NineSlice.cpp
Normal file
108
src/JUI/Widgets/NineSlice.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
#include <JUI/Widgets/NineSlice.hpp>
|
||||
#include <JGL/JGL.h>
|
||||
|
||||
namespace JUI {
|
||||
AABB2D JUI::NineSliceRect::TopLeftQuad() const { return top_left_quad; }
|
||||
|
||||
AABB2D NineSliceRect::TopRightQuad() const { return top_right_quad; }
|
||||
|
||||
AABB2D NineSliceRect::BottomLeftQuad() const { return bottom_left_quad; }
|
||||
|
||||
AABB2D NineSliceRect::BottomRightQuad() const { return bottom_right_quad; }
|
||||
|
||||
AABB2D NineSliceRect::TopQuad() const { return top_quad; }
|
||||
|
||||
AABB2D NineSliceRect::LeftQuad() const { return left_quad; }
|
||||
|
||||
AABB2D NineSliceRect::RightQuad() const { return right_quad; }
|
||||
|
||||
AABB2D NineSliceRect::BottomQuad() const { return bottom_quad; }
|
||||
|
||||
AABB2D NineSliceRect::CenterQuad() const { return center_quad; }
|
||||
|
||||
void NineSliceRect::TopLeftQuad(const AABB2D &quad) { top_left_quad = quad; }
|
||||
|
||||
void NineSliceRect::TopRightQuad(const AABB2D &quad) { top_right_quad = quad; }
|
||||
|
||||
void NineSliceRect::BottomLeftQuad(const AABB2D &quad) { bottom_left_quad = quad; }
|
||||
|
||||
void NineSliceRect::BottomRightQuad(const AABB2D &quad) { bottom_right_quad = quad; }
|
||||
|
||||
void NineSliceRect::TopQuad(const AABB2D &quad) { top_quad = quad;}
|
||||
|
||||
void NineSliceRect::RightQuad(const AABB2D &quad) { right_quad = quad;}
|
||||
|
||||
void NineSliceRect::BottomQuad(const AABB2D &quad) { bottom_quad = quad;}
|
||||
|
||||
void NineSliceRect::LeftQuad(const AABB2D &quad) { left_quad = quad;}
|
||||
|
||||
void NineSliceRect::CenterQuad(const AABB2D &quad) { center_quad = quad;}
|
||||
|
||||
void NineSliceRect::Draw() {
|
||||
Rect::Draw();
|
||||
|
||||
Vector2 abs_pos = GetAbsolutePosition();
|
||||
Vector2 abs_size = GetAbsoluteSize();
|
||||
|
||||
// Draw Top-Left Quad.
|
||||
Vector2 tl_computed_pos = abs_pos;
|
||||
auto tl_quad = TopLeftQuad();
|
||||
JGL::J2D::DrawPartialSprite(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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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});
|
||||
|
||||
// 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});
|
||||
|
||||
// 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});
|
||||
|
||||
// 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});
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
NineSliceRect::NineSliceRect() : Rect(), ImageBase() {
|
||||
Name("NineSliceRect");
|
||||
}
|
||||
|
||||
NineSliceRect::NineSliceRect(Widget *parent) : Rect(parent), ImageBase() {}
|
||||
|
||||
NineSliceRect::NineSliceRect(Widget *parent, JGL::Texture *texture) : Rect(parent), ImageBase(texture) {}
|
||||
}
|
||||
|
@@ -1,41 +1,20 @@
|
||||
#include <JUI/Mixins/Toggleable.hpp>
|
||||
#include <JUI/Widgets/RadioButton.hpp>
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
RadioButton::RadioButton() : Button(), Toggleable() {}
|
||||
namespace JUI {
|
||||
RadioButton::RadioButton() : Button(), Toggleable() {
|
||||
Name("RadioButton");
|
||||
}
|
||||
RadioButton::RadioButton(Widget* parent) : RadioButton() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
void RadioButton::OnToggleOn() {
|
||||
OnToggleOnEvent.Invoke();
|
||||
}
|
||||
|
||||
void RadioButton::OnToggleOff() {
|
||||
OnToggleOffEvent.Invoke();
|
||||
}
|
||||
*/
|
||||
|
||||
void RadioButton::OnClick(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &btn) {
|
||||
Button::OnClick(mouse_pos, btn);
|
||||
Toggleable::Update();
|
||||
/*
|
||||
toggle = !toggle;
|
||||
|
||||
if (toggle) {
|
||||
OnToggleOn();
|
||||
} else {
|
||||
OnToggleOff();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void RadioButton::Update(float delta)
|
||||
{
|
||||
void RadioButton::Update(float delta) {
|
||||
Button::Update(delta);
|
||||
}
|
||||
}
|
@@ -3,64 +3,57 @@
|
||||
#include <jlog/Logger.hpp>
|
||||
|
||||
namespace JUI {
|
||||
Rect::Rect(): Widget(), RectBase() {}
|
||||
Rect::Rect(): Widget(), RectBase() {
|
||||
Name("Rect");
|
||||
}
|
||||
|
||||
Rect::Rect(Widget *parent): Rect() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
void Rect::Draw() {
|
||||
if (!visible)
|
||||
return;
|
||||
|
||||
J2D::Begin();
|
||||
void Rect::PreDraw()
|
||||
{
|
||||
Vector2 abs_pos = GetAbsolutePosition();
|
||||
Vector2 abs_size = GetAbsoluteSize();
|
||||
|
||||
auto root_size = GetFamilyTreeRoot()->GetAbsoluteSize();
|
||||
|
||||
GLint *old_scissor_bounds;
|
||||
bool clip_was_enabled;
|
||||
// TODO: Re-enable clipping
|
||||
if (clips_descendants) {
|
||||
clip_was_enabled = glIsEnabled(GL_SCISSOR_TEST);
|
||||
if (clip_was_enabled)
|
||||
glGetIntegerv(GL_SCISSOR_BOX, old_scissor_bounds);
|
||||
|
||||
float presumed_screen_height = 600;
|
||||
|
||||
float presumed_screen_height = viewport_size.y;
|
||||
glScissor(abs_pos.x, presumed_screen_height-abs_size.y-abs_pos.y, abs_size.x, abs_size.y);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RectBase::Draw(abs_pos, abs_size);
|
||||
|
||||
// Draw Child Elements with scissor clipping still active
|
||||
Widget::Draw();
|
||||
|
||||
void Rect::PostDraw()
|
||||
{
|
||||
// Returns clip to previous state
|
||||
if (clips_descendants)
|
||||
{
|
||||
//glScissor(old_scissor_bounds[0], old_scissor_bounds[1], old_scissor_bounds[2], old_scissor_bounds[3]);
|
||||
glScissor(old_scissor_bounds[0], old_scissor_bounds[1], old_scissor_bounds[2], old_scissor_bounds[3]);
|
||||
|
||||
if (!clip_was_enabled) {}
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
}
|
||||
|
||||
J2D::End();
|
||||
}
|
||||
|
||||
void Rect::InnerDraw() {
|
||||
RectBase::Draw(GetAbsolutePosition(), GetAbsoluteSize());
|
||||
}
|
||||
|
||||
void Rect::Update(float delta) {
|
||||
if (IsMouseInside() && !mouse_inside_debounce)
|
||||
{
|
||||
if (IsMouseInside() && !mouse_inside_debounce) {
|
||||
MouseEnter.Invoke(last_known_mouse_pos);
|
||||
mouse_inside_debounce = true;
|
||||
}
|
||||
|
||||
if (!IsMouseInside() && mouse_inside_debounce)
|
||||
{
|
||||
if (!IsMouseInside() && mouse_inside_debounce) {
|
||||
MouseExit.Invoke(last_known_mouse_pos);
|
||||
mouse_inside_debounce = false;
|
||||
}
|
||||
@@ -68,20 +61,6 @@ namespace JUI {
|
||||
Widget::Update(delta);
|
||||
}
|
||||
|
||||
bool Rect::IsMouseInside() const {
|
||||
float x = last_known_mouse_pos.x;
|
||||
float y = last_known_mouse_pos.y;
|
||||
auto pos = GetAbsolutePosition();
|
||||
auto size = GetAbsoluteSize();
|
||||
|
||||
//DEBUG(std::format("X {} {} Y {} {}", x,pos.x, y,pos.y));
|
||||
|
||||
if (x > pos.x && y > pos.y && x < pos.x + size.x && y < pos.y + size.y){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AABB2D Rect::GetActualRenderBounds() const {
|
||||
|
||||
Vector2 border = {border_width, border_width};
|
||||
@@ -98,6 +77,9 @@ namespace JUI {
|
||||
return { GetAbsolutePosition(), GetAbsoluteSize()};
|
||||
}
|
||||
|
||||
void Rect::Draw() {
|
||||
Widget::Draw();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,24 +1,36 @@
|
||||
#include "JUI/Widgets/Scene.hpp"
|
||||
|
||||
namespace JUI {
|
||||
Scene::Scene(): Widget() {}
|
||||
Scene::Scene(): Widget() {
|
||||
Name("Scene");
|
||||
}
|
||||
|
||||
Vector2 Scene::GetAbsolutePosition() const { return {0,0};}
|
||||
|
||||
Vector2 Scene::GetAbsoluteSize() const { return {(float)viewport_w,(float)viewport_h};}
|
||||
Vector2 Scene::GetAbsoluteSize() const { return viewport_size / ui_scale;}
|
||||
|
||||
void Scene::Draw() {
|
||||
Widget::Draw();
|
||||
}
|
||||
|
||||
void Scene::SetViewportSize(int w, int h) {
|
||||
viewport_w = w;
|
||||
viewport_h = h;
|
||||
J2D::Begin();
|
||||
|
||||
glScalef(ui_scale.x, ui_scale.y, 0.f);
|
||||
Widget::Draw();
|
||||
J2D::End();
|
||||
|
||||
}
|
||||
|
||||
void Scene::Update(float delta) {
|
||||
Widget::Update(delta);
|
||||
}
|
||||
|
||||
void Scene::GlobalUIScale(const Vector2 &value) { ui_scale = value;}
|
||||
|
||||
Vector2 Scene::GlobalUIScale() const { return ui_scale; }
|
||||
|
||||
bool Scene::ObserveMouseMovement(const Vector2 &latest_known_pos) {
|
||||
Vector2 new_pos = latest_known_pos / ui_scale;
|
||||
return Widget::ObserveMouseMovement(new_pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,7 +1,185 @@
|
||||
#include <JUI/Widgets/ScrollingRect.hpp>
|
||||
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
using namespace JUI;
|
||||
|
||||
}
|
||||
void ScrollingRect::RecomputeRenderTarget() {
|
||||
InnerDraw();
|
||||
}
|
||||
|
||||
|
||||
void ScrollingRect::DrawVerticalScrollbar() {
|
||||
|
||||
// TODO: Ratio for size is fine, but the computation for the scrollbar position is just a raw pixel-offset
|
||||
// when it needs to be a size_ratio of the amount scrolled.
|
||||
|
||||
float size_ratio = canvas_height / GetAbsoluteSize().y;
|
||||
float pos_ratio = scroll / canvas_height;
|
||||
|
||||
|
||||
float travel = (GetAbsoluteSize().y * pos_ratio);
|
||||
|
||||
Vector2 scrollbar_base_pos = GetAbsolutePosition() + Vector2(GetAbsoluteSize().x - scrollbar_width, 0);
|
||||
Vector2 scrollbar_base_size = {scrollbar_width, GetAbsoluteSize().y};
|
||||
|
||||
// Draw box for scrollbar to fit into.
|
||||
J2D::FillRect(Colors::LightGray, scrollbar_base_pos, scrollbar_base_size);
|
||||
|
||||
|
||||
// Compute change in size and position that is appropriate.
|
||||
Vector2 scrollbar_pos = scrollbar_base_pos + Vector2(0, travel);
|
||||
Vector2 scrollbar_size = {scrollbar_base_size.x, (scrollbar_base_size.y / size_ratio)};
|
||||
|
||||
J2D::FillRoundedRect(scrollbar_color, scrollbar_pos, scrollbar_size, 4);
|
||||
|
||||
// Draw little square box at the point where the vertical and horizontal scrollbars would meet.
|
||||
//Vector2 lil_box_size = {scrollbar_width, scrollbar_width};
|
||||
//Vector2 lil_box_pos = GetAbsolutePosition() + GetAbsoluteSize() - lil_box_size;
|
||||
//J2D::FillRect(Colors::DarkGray, lil_box_pos, lil_box_size);
|
||||
}
|
||||
|
||||
void ScrollingRect::Draw() {
|
||||
|
||||
// TODO: When `scroll` is 0, we are at the **bottom** of the render target.
|
||||
// TODO: Flip this around so 0 is at the top
|
||||
// TODO: Also flip the scroll-bar around.
|
||||
|
||||
// TODO: Only recompute when something has changed.
|
||||
// Q: How do we know when something has changed?
|
||||
RecomputeRenderTarget();
|
||||
|
||||
J2D::DrawPartialRenderTarget(canvas, GetAbsolutePosition(), {0, 0}, Vector2(GetAbsoluteSize().x, GetAbsoluteSize().y));
|
||||
|
||||
bool canvas_larger_than_widget = canvas_height > GetAbsoluteSize().y;
|
||||
|
||||
if (vertical_scrollbar_enabled && canvas_larger_than_widget) {
|
||||
DrawVerticalScrollbar();
|
||||
}
|
||||
//J2D::DrawString(Colors::Black, std::format("scroll {}, canvas {}", scroll, canvas_height), GetAbsolutePosition().x, GetAbsolutePosition().y, 1, 12);
|
||||
}
|
||||
|
||||
Vector2 ScrollingRect::CanvasPosition() const {
|
||||
return {GetAbsolutePosition().x, GetAbsolutePosition().y - scroll};
|
||||
}
|
||||
|
||||
// Wouldn't inner draw actually render everything onto the RenderTarget?
|
||||
void ScrollingRect::InnerDraw() {
|
||||
|
||||
J2D::Begin(canvas, true);
|
||||
auto saved_pos = position;
|
||||
auto saved_size = size;
|
||||
|
||||
// Kind of a hack to get the child widgets onto the rendertarget at the correct position.
|
||||
// We basically set the position for just long enough to lie to the child widgets.
|
||||
// The amount we offset by is the absolute position.
|
||||
// If you work the formulas out, the child widgets will believe their parent's AbsolutePosition is {0,0}.
|
||||
|
||||
Position(UDim2::FromPixels(-GetAbsolutePosition().x, -GetAbsolutePosition().y + scroll));
|
||||
|
||||
// TODO: Do something with this fake canvas-size?
|
||||
// TODO: How frequently can we update the RenderTarget size.
|
||||
Size({200_percent, 500_percent});
|
||||
Rect::InnerDraw();
|
||||
DrawChildWidgets();
|
||||
|
||||
//J2D::DrawPoint(Colors::Blue, {1,1}, 2);
|
||||
//J2D::DrawPoint(Colors::Blue, Vector2(GetAbsoluteSize()), 2);
|
||||
|
||||
// Set our position back once we're done.
|
||||
Size(saved_size);
|
||||
Position(saved_pos);
|
||||
J2D::End();
|
||||
}
|
||||
|
||||
const Vector2i default_initialize_canvas_size {1024, 4096};
|
||||
|
||||
ScrollingRect::~ScrollingRect() { delete canvas; }
|
||||
|
||||
ScrollingRect::ScrollingRect() : 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)) {
|
||||
//bool success = canvas->SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE::MSAA_8X);
|
||||
}
|
||||
|
||||
JGL::RenderTarget *ScrollingRect::GetCanvas() { return canvas; }
|
||||
|
||||
Vector2i ScrollingRect::CanvasSize() const { return canvas->GetDimensions(); }
|
||||
|
||||
void ScrollingRect::Update(float delta) {
|
||||
//scroll += delta*5;
|
||||
//canvas->Resize(Vector2i(GetAbsoluteSize().x, GetAbsoluteSize().y));
|
||||
Rect::Update(delta);
|
||||
}
|
||||
|
||||
bool ScrollingRect::ObserveKeyInput(Key key, bool pressed) {
|
||||
if (Widget::ObserveKeyInput(key, pressed))
|
||||
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;
|
||||
}
|
||||
|
||||
bool ScrollingRect::CanScroll() const {
|
||||
return canvas_height > GetAbsoluteSize().y;
|
||||
}
|
||||
|
||||
void ScrollingRect::SetScrollAmount(float value) {
|
||||
scroll = Math::Clamp(value, ScrollMinimum(), ScrollMaximum());
|
||||
}
|
||||
|
||||
float ScrollingRect::Scroll(float amount) {
|
||||
SetScrollAmount(scroll + amount);
|
||||
return scroll;
|
||||
}
|
||||
|
||||
float ScrollingRect::ScrollMaximum() const {
|
||||
return Math::Max(0.f, canvas_height-GetAbsoluteSize().y);
|
||||
}
|
||||
|
||||
void ScrollingRect::ScrollToTop() {
|
||||
SetScrollAmount(ScrollMinimum());
|
||||
}
|
||||
|
||||
void ScrollingRect::ScrollToBottom() {
|
||||
SetScrollAmount(ScrollMaximum());
|
||||
}
|
||||
|
||||
float ScrollingRect::ScrollMinimum() const { return 0; }
|
||||
|
||||
float ScrollingRect::GetScrollAmount() const { return scroll; }
|
||||
|
||||
bool ScrollingRect::ObserveMouseWheel(int mwheel) {
|
||||
if (Widget::ObserveMouseWheel(mwheel))
|
||||
return true;
|
||||
|
||||
if (IsMouseInside()) {
|
||||
Scroll(mwheel*scroll_amount);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@
|
||||
namespace JUI
|
||||
{
|
||||
|
||||
Slider::Slider(JUI::Widget *parent)
|
||||
Slider::Slider(JUI::Widget *parent) : Slider()
|
||||
{
|
||||
this->Parent(parent);
|
||||
}
|
||||
@@ -68,8 +68,11 @@ namespace JUI
|
||||
|
||||
float value = translated + minimum;
|
||||
|
||||
if (current != value)
|
||||
ValueChanged(value);
|
||||
|
||||
current = value;
|
||||
std::cout << "slider value: " << value << std::endl;
|
||||
//std::cout << "slider value: " << value << std::endl;
|
||||
}
|
||||
Rect::Update(delta);
|
||||
}
|
||||
@@ -82,9 +85,9 @@ namespace JUI
|
||||
,GetAbsolutePosition().y
|
||||
};
|
||||
/// TODO: Implement internal padding on scrubber element?
|
||||
J2D::Begin();
|
||||
JGL::J2D::FillRect(Colors::Gray, pos, {scrubber_width, GetAbsoluteSize().y});
|
||||
J2D::End();
|
||||
//J2D::Begin();
|
||||
JGL::J2D::FillRect(scrubber_color, pos, {scrubber_width, GetAbsoluteSize().y});
|
||||
//J2D::End();
|
||||
}
|
||||
|
||||
float Slider::Minimum() const { return minimum; }
|
||||
@@ -112,4 +115,14 @@ namespace JUI
|
||||
void Slider::ScrubberColor(const Color4 &color) { scrubber_color = color;}
|
||||
|
||||
void Slider::ScrubberWidth(float width) { scrubber_width = width;}
|
||||
|
||||
Slider::Slider() {
|
||||
Name("Slider");
|
||||
}
|
||||
|
||||
void Slider::SetDragging(bool value) {
|
||||
dragging = value;
|
||||
}
|
||||
|
||||
bool Slider::Dragging() const { return dragging; }
|
||||
}
|
@@ -2,7 +2,9 @@
|
||||
|
||||
|
||||
namespace JUI {
|
||||
Text::Text() : Widget(), TextBase() {}
|
||||
Text::Text() : Widget(), TextBase() {
|
||||
Name("Text");
|
||||
}
|
||||
|
||||
Text::Text(Widget* parent) : Text()
|
||||
{
|
||||
@@ -11,7 +13,7 @@ namespace JUI {
|
||||
|
||||
Text::~Text() {}
|
||||
Vector2 Text::GetAbsolutePosition() const {
|
||||
auto parent =this->GetParent();
|
||||
auto parent = this->GetParent();
|
||||
if (parent)
|
||||
return parent->GetAbsolutePosition();
|
||||
//return {0,0};
|
||||
@@ -24,12 +26,14 @@ namespace JUI {
|
||||
//return {0, 0};
|
||||
}
|
||||
|
||||
void Text::Draw()
|
||||
{
|
||||
void Text::Draw() {
|
||||
auto abs_pos = this->GetAbsolutePosition();
|
||||
auto abs_size = this->GetAbsoluteSize();
|
||||
|
||||
TextBase::Draw(abs_pos, abs_size);
|
||||
auto pos_pad = GetAbsolutePaddingTopLeft();
|
||||
auto size_pad = GetAbsolutePaddingBottomRight();
|
||||
|
||||
TextBase::Draw(abs_pos+pos_pad, abs_size-size_pad);
|
||||
|
||||
Widget::Draw();
|
||||
}
|
||||
|
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
TextButton::TextButton() : Button(), TextBase() {}
|
||||
TextButton::TextButton() : Button(), TextBase() {
|
||||
Name("TextButton");
|
||||
}
|
||||
TextButton::TextButton(Widget* parent) : TextButton() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
@@ -14,6 +16,7 @@ namespace JUI
|
||||
|
||||
void TextButton::Draw() {
|
||||
Button::Draw();
|
||||
TextBase::Draw(this->GetAbsolutePosition(), this->GetAbsoluteSize());
|
||||
// TODO: Factor in padding here.
|
||||
TextBase::Draw(this->GetAbsolutePosition()+GetAbsolutePaddingTopLeft(), this->GetAbsoluteSize()-GetAbsolutePaddingBottomRight());
|
||||
}
|
||||
}
|
||||
|
253
src/JUI/Widgets/TextInputForm.cpp
Normal file
253
src/JUI/Widgets/TextInputForm.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
#include <JUI/Widgets/TextInputForm.hpp>
|
||||
#include <ReWindow/InputService.h>
|
||||
|
||||
namespace JUI {
|
||||
TextInputForm::TextInputForm() : TextRect(), Clickable() {
|
||||
Name("TextInputForm");
|
||||
}
|
||||
TextInputForm::TextInputForm(Widget* parent) : TextInputForm() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
bool TextInputForm::ObserveMouseMovement(const Vector2 &latest_known_pos) {
|
||||
return Widget::ObserveMouseMovement(latest_known_pos);
|
||||
}
|
||||
|
||||
std::string TextInputForm::GetAutocompleteText() const { return autocomplete_text;}
|
||||
|
||||
void TextInputForm::SetAutoCompleteText(const std::string &text) { autocomplete_text = text;}
|
||||
|
||||
Color4 TextInputForm::GetAutocompleteTextColor() const { return autocomplete_color; }
|
||||
|
||||
void TextInputForm::SetAutocompleteTextColor(const Color4 &color) { autocomplete_color = color; }
|
||||
|
||||
bool TextInputForm::HideAutocompleteOnSelect() const { return hide_autocomplete_on_select; }
|
||||
|
||||
void TextInputForm::SetHideAutocompleteOnSelect(bool hide) { hide_autocomplete_on_select = hide; }
|
||||
|
||||
bool TextInputForm::AutocompleteTextEnabled() const { return autocomplete_text_enabled; }
|
||||
|
||||
void TextInputForm::SetAutocompleteTextEnabled(bool enabled) { autocomplete_text_enabled = enabled; }
|
||||
|
||||
bool TextInputForm::ObserveMouseInput(MouseButton btn, bool pressed) {
|
||||
|
||||
if (pressed && btn == MouseButton::Left) {
|
||||
|
||||
if (IsMouseInside()) {
|
||||
if (!focused) {
|
||||
OnSelect.Invoke();
|
||||
}
|
||||
focused = true;
|
||||
return true;
|
||||
} else {
|
||||
if (focused)
|
||||
OnDeselect.Invoke();
|
||||
focused = false;
|
||||
}
|
||||
|
||||
}
|
||||
return Widget::ObserveMouseInput(btn, pressed);
|
||||
}
|
||||
|
||||
void TextInputForm::InnerDraw()
|
||||
{
|
||||
|
||||
Rect::InnerDraw();
|
||||
|
||||
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),
|
||||
pad_top.Pixels + (pad_left.Scale * abs_size.y)
|
||||
};
|
||||
|
||||
Vector2 pad_shrinks_size_by = {
|
||||
pad_right.Pixels + (pad_left.Scale * abs_size.x),
|
||||
pad_bottom.Pixels + (pad_right.Scale * abs_size.y)
|
||||
};
|
||||
|
||||
Vector2 pos = abs_pos + pad_shifts_pos_by;
|
||||
Vector2 size = abs_pos - (pad_shrinks_size_by*2.f);
|
||||
|
||||
if (!focused || !hide_autocomplete_on_select)
|
||||
TextBase::Draw(pos, size, autocomplete_text, this->text_size, autocomplete_color);
|
||||
|
||||
TextBase::Draw(pos, size, content, text_size, text_color);
|
||||
|
||||
//TextRect::InnerDraw();
|
||||
}
|
||||
|
||||
void TextInputForm::Draw() {
|
||||
TextRect::Draw();
|
||||
}
|
||||
|
||||
void TextInputForm::Update(float elapsed) {
|
||||
TextRect::Update(elapsed);
|
||||
|
||||
// TODO: Make cursor actually blink
|
||||
if (focused) {
|
||||
cursor_blink_time += elapsed;
|
||||
|
||||
if (cursor_blink_time > 1.f)
|
||||
cursor_blink_time = 0.f;
|
||||
|
||||
if (cursor_blink_time > 0.5f) {
|
||||
std::string result = input_buffer;
|
||||
|
||||
result.insert(cursor_position, 1, '|');
|
||||
SetContent(result);
|
||||
} else {
|
||||
SetContent(input_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string uppercase(const std::string& input) {
|
||||
std::string s = input;
|
||||
std::ranges::transform(s, s.begin(), ::toupper);
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string lowercase(const std::string& input) {
|
||||
std::string s = input;
|
||||
std::ranges::transform(s, s.begin(), ::tolower);
|
||||
return s;
|
||||
}
|
||||
|
||||
void TextInputForm::MoveCursorLeft() {
|
||||
if (cursor_position > 0)
|
||||
cursor_position--;
|
||||
}
|
||||
|
||||
void TextInputForm::MoveCursorRight() {
|
||||
if (cursor_position < CursorMaxPosition()-1)
|
||||
cursor_position++;
|
||||
}
|
||||
|
||||
unsigned int TextInputForm::CursorMaxPosition() const {
|
||||
return input_buffer.length();
|
||||
}
|
||||
|
||||
void TextInputForm::SetCursorPosition(unsigned int pos) {
|
||||
pos = Math::Clamp<unsigned int>(pos, 0, CursorMaxPosition());
|
||||
cursor_position = pos;
|
||||
}
|
||||
|
||||
void TextInputForm::SendInput(bool clear_input) {
|
||||
OnReturn.Invoke(input_buffer);
|
||||
|
||||
if (clear_input) {
|
||||
input_buffer = "";
|
||||
cursor_position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the given key is a 'special' key, which should not be typed into the buffer, and has no other special function.
|
||||
bool IsNonAlphanumerosymbolic(const Key& key) {
|
||||
return key == Keys::LeftShift || key == Keys::RightShift || key == Keys::LeftControl || key == Keys::RightControl
|
||||
|| key == Keys::LeftAlt || key == Keys::RightAlt || key == Keys::Super || key == Keys::Escape
|
||||
|| key == Keys::F1 || key == Keys::F2 || key == Keys::F3 || key == Keys::F4 || key == Keys::F5 || key == Keys::F6
|
||||
|| key == Keys::F7 || key == Keys::F8 || key == Keys::F9 || key == Keys::F10 || key == Keys::F11 || key == Keys::F12;
|
||||
}
|
||||
|
||||
void TextInputForm::Copy() {}
|
||||
void TextInputForm::Paste() {}
|
||||
void TextInputForm::Cut() {}
|
||||
void TextInputForm::Delete() { }
|
||||
void TextInputForm::Backspace() {
|
||||
if (cursor_position > 0) {
|
||||
input_buffer = input_buffer.erase(cursor_position-1, 1);
|
||||
cursor_position--;
|
||||
}
|
||||
}
|
||||
void TextInputForm::PushStringToCurrentPlaceInInputBuffer(const std::string& snippet) {
|
||||
input_buffer = input_buffer.insert(cursor_position, snippet);
|
||||
cursor_position += snippet.length();
|
||||
}
|
||||
void TextInputForm::PushKeyToCurrentPlaceInInputBuffer(const Key& key) {
|
||||
std::string insertion = lowercase(key.Mnemonic);
|
||||
if (InputService::IsKeyDown(Keys::LeftShift) || InputService::IsKeyDown(Keys::RightShift)) {
|
||||
insertion = uppercase(insertion);
|
||||
// characters that don't work with std::toupper, so we do it manually.
|
||||
if (key == Keys::One) insertion = "!";
|
||||
if (key == Keys::Two) insertion = "@";
|
||||
if (key == Keys::Three) insertion = "#";
|
||||
if (key == Keys::Four) insertion = "$";
|
||||
if (key == Keys::Five) insertion = "%";
|
||||
if (key == Keys::Six) insertion = "&";
|
||||
if (key == Keys::Seven) insertion = "'";
|
||||
if (key == Keys::Eight) insertion = "*";
|
||||
if (key == Keys::Nine) insertion = "(";
|
||||
if (key == Keys::Zero) insertion = ")";
|
||||
}
|
||||
PushStringToCurrentPlaceInInputBuffer(insertion);
|
||||
}
|
||||
|
||||
bool TextInputForm::ObserveKeyInput(Key key, bool pressed) {
|
||||
Widget::ObserveKeyInput(key, pressed);
|
||||
if (!pressed || !focused) return false;
|
||||
|
||||
if (key == Keys::Return || key == Keys::NumPadReturn) {
|
||||
SendInput(clear_text_on_return);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == Keys::LeftArrow) {
|
||||
MoveCursorLeft();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == Keys::RightArrow) {
|
||||
MoveCursorRight();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (InputService::IsKeyDown(Keys::LeftControl)) {
|
||||
if (key == Keys::C) { Copy(); return true; }
|
||||
if (key == Keys::V) { Paste(); return true; }
|
||||
if (key == Keys::X) { Cut(); return true; }
|
||||
}
|
||||
|
||||
if (IsNonAlphanumerosymbolic(key)) return false;
|
||||
if (blacklist.contains(key.Mnemonic)) return false;
|
||||
if (key == Keys::Backspace) {Backspace(); return true; }
|
||||
|
||||
// No other condition met, so we assume we should push this key into the input buffer.
|
||||
PushKeyToCurrentPlaceInInputBuffer(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextInputForm::HasFocus() const { return focused; }
|
||||
|
||||
void TextInputForm::SetFocused(bool focused) { this->focused = focused;}
|
||||
|
||||
void TextInputForm::GrabFocus() { SetFocused(true); }
|
||||
|
||||
void TextInputForm::DropFocus() { SetFocused(false); }
|
||||
|
||||
bool TextInputForm::ClearTextOnReturn() const { return clear_text_on_return; }
|
||||
|
||||
void TextInputForm::ClearTextOnReturn(bool value) { clear_text_on_return = value;}
|
||||
|
||||
bool TextInputForm::DropFocusOnReturn() const { return drop_focus_on_return;}
|
||||
|
||||
void TextInputForm::DropFocusOnReturn(bool value) { drop_focus_on_return = value;}
|
||||
|
||||
std::set<std::string> TextInputForm::GetBlacklist() const { return blacklist;}
|
||||
|
||||
std::string TextInputForm::InputBuffer() const { return input_buffer;}
|
||||
|
||||
void TextInputForm::SetInputBuffer(const std::string &value) { input_buffer = value;}
|
||||
|
||||
void TextInputForm::ClearInputBuffer() { SetInputBuffer(""); }
|
||||
|
||||
void TextInputForm::SetBlacklist(const std::set<std::string> &value) {
|
||||
blacklist = value;
|
||||
}
|
||||
|
||||
void TextInputForm::AddToBlacklist(const std::string& value) {
|
||||
blacklist.insert(value);
|
||||
}
|
||||
|
||||
}
|
@@ -1,19 +1,22 @@
|
||||
#include "JUI/Widgets/TextRect.hpp"
|
||||
|
||||
namespace JUI {
|
||||
TextRect::TextRect() : Rect(), TextBase() {}
|
||||
TextRect::TextRect() : Rect(), TextBase() {
|
||||
Name("TextRect");
|
||||
}
|
||||
TextRect::TextRect(Widget* parent) : TextRect() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
void TextRect::Update(float delta) {
|
||||
|
||||
if (fit_text)
|
||||
if (fit_size_to_text)
|
||||
{
|
||||
|
||||
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),
|
||||
@@ -25,7 +28,7 @@ namespace JUI {
|
||||
};
|
||||
|
||||
Vector2 size = this->set_font.MeasureString(this->content, this->text_size);
|
||||
size += pad_shrinks_size_by*2.f;
|
||||
//size += pad_shrinks_size_by*2.f;
|
||||
|
||||
if (abs_size.x < size.x)
|
||||
this->size.X.Pixels = size.x;
|
||||
@@ -39,25 +42,20 @@ namespace JUI {
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TextRect::Draw() {
|
||||
Rect::Draw();
|
||||
|
||||
|
||||
void TextRect::InnerDraw()
|
||||
{
|
||||
Rect::InnerDraw();
|
||||
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),
|
||||
pad_top.Pixels + (pad_left.Scale * abs_size.y)
|
||||
};
|
||||
Vector2 pad_shrinks_size_by = {
|
||||
pad_right.Pixels + (pad_left.Scale * abs_size.x),
|
||||
pad_bottom.Pixels + (pad_right.Scale * abs_size.y)
|
||||
};
|
||||
Vector2 pos_pad = GetAbsolutePaddingTopLeft();
|
||||
Vector2 size_pad = GetAbsolutePaddingBottomRight();
|
||||
|
||||
TextBase::Draw(abs_pos + pad_shifts_pos_by, abs_size - pad_shrinks_size_by*2);
|
||||
TextBase::Draw(abs_pos + pos_pad, abs_size - size_pad);
|
||||
}
|
||||
|
||||
void TextRect::Draw() {
|
||||
Rect::Draw();
|
||||
}
|
||||
|
||||
|
||||
|
1
src/JUI/Widgets/Tooltip.cpp
Normal file
1
src/JUI/Widgets/Tooltip.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include <JUI/Widgets/Tooltip.hpp>
|
@@ -3,37 +3,17 @@
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
namespace JUI
|
||||
{
|
||||
namespace JUI {
|
||||
Window::Window() : Widget(), Clickable(), Hoverable(), RectBase(), Draggable(), Resizable(), Dockable() {
|
||||
Name("Window");
|
||||
this->Position({200, 200, 0, 0});
|
||||
this->Size({400, 200, 0, 0});
|
||||
min_size = {400, 200};
|
||||
this->BGColor({64,64,64,255});
|
||||
this->BorderColor({92,92,192});
|
||||
this->SetBorderWidth(2);
|
||||
this->BGColor(Style::Window::OutlineColor);
|
||||
this->BorderColor(Style::Window::OutlineColor);
|
||||
this->BorderWidth(Style::Window::OutlineWidth);
|
||||
this->BorderMode(BorderMode::Middle);
|
||||
|
||||
//this->OnHoverEvent += [&] (Vector2 MousePos) {
|
||||
|
||||
//};
|
||||
|
||||
//this->OnExitEvent += [&] (Vector2 MousePos) {
|
||||
// hovered;
|
||||
//};
|
||||
|
||||
/*
|
||||
this->OnClickEvent += [&] (Vector2 dummy, MouseButton btn) {
|
||||
if (draggable && btn == MouseButton::Left) {
|
||||
DEBUG("Window draggable")
|
||||
SetDrag(true);
|
||||
}
|
||||
|
||||
if (resizable && btn == MouseButton::Right)
|
||||
DEBUG("Window resizable")
|
||||
};
|
||||
*/
|
||||
|
||||
// TODO: Move out of Event callback
|
||||
this->OnReleaseEvent += [&] (Vector2 dummy, MouseButton btn, bool dummy3) {
|
||||
if (dragging) {
|
||||
@@ -43,60 +23,74 @@ namespace JUI
|
||||
|
||||
Viewport = new Rect(this);
|
||||
Viewport->Name("Viewport");
|
||||
Viewport->BGColor({64,64,64, 0});
|
||||
|
||||
Viewport->Size({0, -20, 1, 1});
|
||||
Viewport->Position({0, 20, 0, 0});
|
||||
Viewport->BGColor(Style::Window::ViewportBackgroundColor);
|
||||
Viewport->Size({0, -titlebar_height, 1, 1});
|
||||
Viewport->Position({0, titlebar_height, 0, 0});
|
||||
// TODO: Viewport->AnchorPoint({0.f, 0.f});
|
||||
Viewport->BorderColor({128, 128, 128, 255});
|
||||
Viewport->SetBorderWidth(0);
|
||||
Viewport->BorderWidth(0);
|
||||
//Viewport->Padding(1_px);
|
||||
|
||||
// TODO: Viewport->SetBorderRadius(0);
|
||||
|
||||
Topbar = new Rect(this);
|
||||
Topbar->Position({0_px, 0_px});
|
||||
Topbar->Size({100_percent, 20_px});
|
||||
Topbar->BGColor({92,92,192, 255});
|
||||
Topbar->Size({100_percent, UDim(titlebar_height, 0)});
|
||||
//Topbar->BGColor({92,92,192, 255});
|
||||
Topbar->BGColor(Colors::Transparent);
|
||||
Topbar->BorderColor({128, 128, 128, 0});
|
||||
Topbar->SetBorderWidth(0);
|
||||
Topbar->BorderWidth(0);
|
||||
|
||||
TitleLabel = new Text(Topbar);
|
||||
TitleLabel->Center();
|
||||
TitleLabel->SetContent(title);
|
||||
TitleLabel->SetTextSize(titlebar_height);
|
||||
TitleLabel->SetTextSize(title_font_size);
|
||||
|
||||
// TODO: Pull out this circle image generation code, make a Circle widget.
|
||||
|
||||
// TODO: auto* list = new HorizontalListLayout(Topbar);
|
||||
|
||||
// TODO: exit_btn
|
||||
exit_btn = new TextButton(Topbar);
|
||||
|
||||
auto* exb_tex = new Texture( Vector2i(titlebar_height, titlebar_height));
|
||||
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 {
|
||||
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
|
||||
};
|
||||
|
||||
std::array<float, 2> exb_circle_radii {
|
||||
(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);
|
||||
J2D::End();
|
||||
delete exb_rt;
|
||||
|
||||
exit_btn = new ImageButton(Topbar);
|
||||
exit_btn->Content(exb_tex);
|
||||
//exit_btn->AnchorPoint({1.f, 0.f});
|
||||
//exit_btn->Size({30_px, 100_percent});
|
||||
exit_btn->Size({titlebar_height, titlebar_height, 0, 0});
|
||||
//exit_btn->BorderColor({128, 128, 128, 255});
|
||||
//exit_btn->SetBaseColor({192, 64, 64, 255});
|
||||
//exit_btn->SetHoverColor({255, 0, 0, 255});
|
||||
//exit_btn->BGColor({192, 64, 64, 255});
|
||||
exit_btn->SetContent("X");
|
||||
exit_btn->BGColor({92,92,192, 255});
|
||||
//JGL::Font FreeSans
|
||||
//exit_btn_text->SetFont(TitleLabel->GetFont());
|
||||
exit_btn->SetTextSize(titlebar_height);
|
||||
exit_btn->SetTextColor({255, 0, 0});
|
||||
//exit_btn->Center();
|
||||
|
||||
exit_btn->OnClickEvent += [&] (auto... _) {
|
||||
exit_btn->BGColor(Colors::Transparent);
|
||||
exit_btn->BGColors(Colors::Transparent, Colors::Transparent, Colors::Transparent, Colors::Transparent);
|
||||
exit_btn->BorderWidth(0.f);
|
||||
exit_btn->BaseImageColor(Colors::Reds::LightCoral);
|
||||
exit_btn->HoveredImageColor(Colors::Red);
|
||||
exit_btn->PressedImageColor(Colors::Reds::DarkRed);
|
||||
|
||||
exit_btn->OnReleaseEvent += [&] (auto... _) {
|
||||
this->Visible(false);
|
||||
};
|
||||
|
||||
jlog::Debug(std::format("{} {} {} {}", Topbar->Size().X.Pixels, Topbar->Size().Y.Pixels, Topbar->Size().X.Scale,
|
||||
Topbar->Size().Y.Scale));
|
||||
//min_size = exit_btn->GetSize() + Topbar->GetSize() + TitleLabel->Size();
|
||||
//= Topbar->GetSize();//+ exit_btn->Size();
|
||||
// TODO: fs_btn
|
||||
|
||||
|
||||
}
|
||||
|
||||
Window::Window(Widget* parent) : Window() {
|
||||
@@ -117,11 +111,11 @@ namespace JUI
|
||||
|
||||
bool Window::IsResizable() const { return resizable; }
|
||||
|
||||
Text *Window::GetTitleInstance() { return TitleLabel; }
|
||||
Text *Window::TitleInstance() { return TitleLabel; }
|
||||
|
||||
Rect *Window::GetTopbarInstance() { return Topbar; }
|
||||
Rect *Window::TopbarInstance() { return Topbar; }
|
||||
|
||||
Rect *Window::GetViewportInstance() { return Viewport; }
|
||||
Rect *Window::ViewportInstance() { return Viewport; }
|
||||
|
||||
void Window::SetDrag(bool d) {
|
||||
Draggable::SetDrag(d);
|
||||
@@ -130,7 +124,7 @@ namespace JUI
|
||||
|
||||
void Window::SetTitleFont(const Font& f) {
|
||||
TitleLabel->SetFont(f);
|
||||
exit_btn->SetFont(f);
|
||||
//exit_btn->SetFont(f);
|
||||
}
|
||||
|
||||
void Window::OnClick(const Vector2& mouse_pos, const MouseButton& btn)
|
||||
@@ -138,10 +132,10 @@ namespace JUI
|
||||
Clickable::OnClick(mouse_pos, btn);
|
||||
clicked = true;
|
||||
|
||||
if (draggable && btn == MouseButton::Left)
|
||||
if (draggable && btn == MouseButton::Left && TitleInstance()->IsMouseInside())
|
||||
this->SetDrag(true);
|
||||
|
||||
if (resizable && btn == MouseButton::Right) {
|
||||
if (resizable && btn == MouseButton::Right && TitleInstance()->IsMouseInside()) {
|
||||
this->StartResizing(last_known_mouse_pos);
|
||||
this->SetResize(true);
|
||||
size_when_restart_began = Size();
|
||||
@@ -150,20 +144,35 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Window::DragTo(const Vector2& pos)
|
||||
{
|
||||
this->Position(UDim2{(int) pos.x, (int) pos.y, 0, 0});
|
||||
}
|
||||
|
||||
void Window::Update(float delta) {
|
||||
if (dragging) {
|
||||
//jlog::Debug(std::format("mpos {} {}", last_known_mouse_pos.x, last_known_mouse_pos.y));
|
||||
Vector2 mpos = last_known_mouse_pos - initial_drag_offset;
|
||||
this->Position(UDim2{(int) mpos.x, (int) mpos.y, 0, 0});
|
||||
// Move the window with the mouse, accounting for initial offset of window-mouse when we first clicked.
|
||||
Vector2 goal = last_known_mouse_pos - initial_drag_offset;
|
||||
DragTo(goal);
|
||||
}
|
||||
|
||||
if (resizing) {
|
||||
@@ -188,7 +197,6 @@ namespace JUI
|
||||
float needed_amt_to_resize = min_size.y - abs_size.y;
|
||||
Size(Size() + UDim2(0, needed_amt_to_resize, 0, 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Widget::Update(delta);
|
||||
@@ -203,7 +211,8 @@ 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());
|
||||
}
|
||||
@@ -216,10 +225,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) {
|
||||
GetTitleInstance()->SetContent(title);
|
||||
TitleInstance()->SetContent(title);
|
||||
}
|
||||
|
||||
void Window::MinSize(const Vector2 &constraint) {
|
||||
@@ -242,7 +254,65 @@ namespace JUI
|
||||
resizable = value;
|
||||
}
|
||||
|
||||
TextButton *Window::GetExitButtonInstance() {
|
||||
ImageButton* Window::ExitButtonInstance() {
|
||||
return exit_btn;
|
||||
}
|
||||
|
||||
void Window::LayoutControlsLeft() {
|
||||
exit_btn->AnchorPoint({0.f, 0.f});
|
||||
exit_btn->Position({0_px, 0_px});
|
||||
}
|
||||
|
||||
void Window::LayoutControlsRight() {
|
||||
exit_btn->AnchorPoint({1.f, 0.f});
|
||||
exit_btn->Position({100_percent, 0_px});
|
||||
}
|
||||
|
||||
Vector2 Window::AbsoluteViewportPosition() const {
|
||||
return Viewport->GetAbsolutePosition();
|
||||
}
|
||||
|
||||
Vector2 Window::AbsoluteViewportSize() const {
|
||||
return Viewport->GetAbsoluteSize();
|
||||
}
|
||||
|
||||
AABB2D Window::AbsoluteViewportBounds() const {
|
||||
return {AbsoluteViewportPosition(), AbsoluteViewportSize()};
|
||||
}
|
||||
|
||||
void Window::UpdateInternalWidgetsTitlebarHeight() {
|
||||
Viewport->Size({0, -titlebar_height, 1, 1});
|
||||
Viewport->Position({0, titlebar_height, 0, 0});
|
||||
|
||||
Topbar->Size({100_percent, UDim(titlebar_height, 0)});
|
||||
exit_btn->Size({titlebar_height, titlebar_height, 0, 0});
|
||||
}
|
||||
|
||||
void Window::TitlebarHeight(int height) {
|
||||
titlebar_height = height;
|
||||
UpdateInternalWidgetsTitlebarHeight();
|
||||
}
|
||||
|
||||
int Window::TitlebarHeight() const {
|
||||
return titlebar_height;
|
||||
}
|
||||
|
||||
void Window::CornerRounding(float radius) {
|
||||
RectBase::CornerRounding(radius);
|
||||
}
|
||||
|
||||
void Window::SetOpen(bool value) {
|
||||
open = value;
|
||||
Visible(open);
|
||||
}
|
||||
|
||||
void Window::Toggle() {
|
||||
SetOpen(!IsOpen());
|
||||
}
|
||||
|
||||
void Window::Close() { SetOpen(false); }
|
||||
|
||||
void Window::Open() { SetOpen(true); }
|
||||
|
||||
bool Window::IsOpen() const { return open;}
|
||||
}
|
Reference in New Issue
Block a user