87 Commits

Author SHA1 Message Date
251daa4070 Work-In-Progress tween code. 2025-02-10 14:45:59 -05:00
fc65e9d229 ImageButton & change window exit to red circle. 2025-02-08 16:04:37 -05:00
2aaba5c1f7 Implement Tween class interface. 2025-02-08 15:35:01 -05:00
97edc67fad Tweening animation test 1. 2025-02-08 14:44:43 -05:00
1053a32317 Renamed several functions, testing tween support. 2025-02-08 04:07:00 -05:00
9a70839d2e Add JUI::Window:AbsoluteViewportPosition and AbsoluteViewportSize 2025-02-08 03:24:39 -05:00
578d15200c Add JUI::Window:LayoutControls Left & Right 2025-02-07 04:09:51 -05:00
15a2bfe667 Merge pull request 'test-jgl-changes' (#57) from test-jgl-changes into master
Reviewed-on: #57
2025-02-07 03:48:34 -05:00
d204d991c1 More Scroller Testing 2025-02-07 03:47:44 -05:00
29f5125ccc Test MSAA 2025-02-07 02:42:30 -05:00
c44e5c830c Logger Window Test!! 2025-02-07 02:40:13 -05:00
c78f9a045c JGL::RenderTarget::Resize appears to have an issue. 2025-02-07 01:56:25 -05:00
7f2477677a Refactoring entire render logic to support ScrollingRect, got some results, but still having issues. 2025-02-07 01:48:22 -05:00
fe454209f7 Other small edits 2025-02-04 19:59:03 -05:00
5673ce3b9e Changes to WIP ScrollingRect 2025-02-04 19:53:43 -05:00
02cd200114 Changes to WIP ScrollingRect 2025-02-04 19:53:40 -05:00
32d9615001 Change behavior of tooltip and UtilityBar 2025-02-04 19:51:42 -05:00
5efbe3fea3 Migrate to latest JGL 2025-02-04 19:51:19 -05:00
1520db8dcd Merge remote-tracking branch 'origin/master' 2025-02-04 19:50:59 -05:00
663355726b Refactor RectBase to allow blit to RenderTarget 2025-02-04 19:43:57 -05:00
83ffecb91d Hoverable class no longer drives tooltips. Rather, a tooltip widget can be parented to any widget. 2025-02-04 19:43:36 -05:00
135a1564f2 Remove Canvas class 2025-02-04 19:41:21 -05:00
d3f29e2072 Fix update chain in Button class 2025-02-04 19:41:11 -05:00
5f7a2326bd CHange showcase icon. 2025-02-04 16:19:33 -06:00
8acf32f140 Update README.md 2025-02-04 17:09:18 -05:00
d89d74052c Update README.md 2025-02-04 17:08:49 -05:00
2e65380ae6 Update README.md 2025-02-04 17:07:45 -05:00
5bf6e2cabc Add 9slice sample image. 2025-02-03 00:15:15 -05:00
593ba22626 Implemented NineSliceRect widget. 2025-02-03 00:13:56 -05:00
27cde1f37c Implemented NineSliceRect widget. 2025-02-02 23:59:15 -05:00
103813e9c1 Fix bug in slider. 2025-01-28 18:59:20 -05:00
0ef7ec2a7f Migrate to latest ReWindow and JGL 2025-01-28 18:35:34 -05:00
0cd3fbfc17 Bugfixes for Slider and Window widget. 2025-01-28 18:11:35 -05:00
e7e469864f Cleanup & Stater
Make it so when you modify any element of the text that would necessitate redrawing the text we do.
2025-01-21 11:07:22 -05:00
0f00cfa48d Merge branch 'render_targets_for_text' 2025-01-20 20:49:42 -05:00
c9a9c6aeb5 Fixed.
Update to latest JGL & check if bounds is zero because that breaks *everything* to do with RenderTargets.
2025-01-20 20:49:21 -05:00
355e9f975e Edits to ScrollingRect. 2025-01-19 13:54:20 -05:00
5293a845ad ???? 2025-01-19 01:58:59 -05:00
a31ee731d2 Merge remote-tracking branch 'origin/master' 2025-01-16 00:07:26 -05:00
389f266722 Fixed some functions, will check into the rest in the morning. 2025-01-16 00:07:07 -05:00
Redacted
e32bce6b50 Update CMakeLists.txt 2025-01-15 23:02:08 -05:00
93632faaed Text Input Form Autocomplete text. 2025-01-09 13:19:20 -05:00
e11b8c8c50 Text Input Form OnReturn event. 2025-01-08 15:35:14 -05:00
2e0d75391e Text Input Form proper cursor behavior. 2025-01-08 13:53:11 -05:00
bc8ba3ed02 Implementing Text Input Form and it's features. 2025-01-07 19:09:37 -05:00
9867f49ebf Fix issue with default-initialization of fonts as garbage data in JUI. 2024-12-09 18:07:32 -05:00
c5766aa358 Merge remote-tracking branch 'origin/master' 2024-12-09 17:40:23 -05:00
3d22d683bc Migrate to latest JGL. 2024-12-09 17:40:16 -05:00
432a5990ce Roll back to code from prelease-41. Fix segfault later 2024-12-09 13:12:35 -05:00
2a7b562b8f Remove NonFree fonts 2024-12-04 15:31:47 -05:00
936c1902d0 Set sane default members on Hoverable 2024-12-04 15:05:07 -05:00
e8eb018959 Partially Completed Edits xddd 2024-12-04 14:41:53 -05:00
465d7052b7 pushing 2024-12-04 14:30:48 -05:00
ff84118b10 Fuck 2024-11-25 20:10:45 -05:00
9b3bf93ea1 Current State 2024-11-25 17:51:41 -05:00
0ffb750f82 Oops 2024-11-25 16:35:13 -06:00
423dc188cd Incremental Improvements 2024-11-22 16:45:14 -05:00
3a062a2709 Implement ZIndex draw sorting. 2024-11-22 16:02:55 -05:00
146b8190b0 Another Segfault 2024-11-21 13:40:01 -05:00
1cb0c1299a Segmentation fault on file->OnClickEvent callback 2024-11-21 12:40:48 -05:00
3ad9c615b6 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	include/JUI/Widgets/Button.hpp
2024-11-19 19:15:33 -05:00
19e066c835 Several minor changes 2024-11-19 19:15:18 -05:00
7f38836a12 Update Button.hpp 2024-11-13 15:51:31 -05:00
d2acf6f7ba Update CMakeLists.txt 2024-11-13 15:18:10 -05:00
3e636a5821 Implement LayoutOrder getter and setter on VerticalListLayout and HorizontalListLayout 2024-11-01 00:20:39 -04:00
8318c5ed35 Implementing boilerplate :)! x2 2024-10-31 15:12:07 -04:00
a5f2564139 Implementing boilerplate :)! 2024-10-31 15:11:56 -04:00
0ce539862c Minor adjustments, add file stubs for classes to come! 2024-10-31 14:49:18 -04:00
e7a00bbd9b Moved TextButton to it's own class 2024-10-31 14:48:47 -04:00
f84d3c7f16 Extra doxygen annotations on Widget 2024-10-31 14:48:28 -04:00
9f7fd8e142 Add jlog instance for logging specifically from the UI layer 2024-10-31 14:48:09 -04:00
1361655557 Annotation for Image & ImageBase, add files for ImageRect class 2024-10-31 14:47:39 -04:00
081f948dc9 Small adjustments to Clickable logic. 2024-10-31 14:46:31 -04:00
50ed453f64 Implemented first-iteration slider class 2024-10-31 14:46:06 -04:00
dcf65516cc Doxygen annotation added to Button class 2024-10-31 12:55:27 -04:00
8a9ceb87e2 Partial Work 2024-10-28 18:53:24 -04:00
07c3433dd7 Fixed Rect outline pixel misalignment. 2024-10-28 16:29:29 -04:00
9453a57616 Implement Button Color changing logic 2024-10-28 16:05:42 -04:00
781c9882d4 Sending Up Work 2024-10-23 14:16:54 -04:00
63ff2e6ef2 Current Working State 2024-10-14 20:42:05 -04:00
53a1d7bd9c Refactoring in progress x2 2024-10-13 17:52:33 -04:00
ca75a143d3 Laptop work 2024-10-12 20:52:28 -05:00
07725ded44 Refactoring in progress 2024-10-12 19:35:17 -04:00
67fa678ca6 Refactored Hoverable 2024-10-12 00:55:22 -04:00
1065933fcb Refactoring and cleaning up sections. 2024-10-11 15:08:37 -04:00
cfce294bf2 Migrate to latest dependency releases, and fixed breaking changes. 2024-10-11 11:45:27 -04:00
48aaff669e Migrate to latest dependency releases, and fixed breaking changes. 2024-10-11 11:45:23 -04:00
79 changed files with 3382 additions and 1057 deletions

View File

@@ -31,29 +31,34 @@ 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-5.zip
)
CPMAddPackage(
NAME Event
URL https://git.redacted.cc/josh/Event/archive/Release-10.zip
URL https://git.redacted.cc/josh/Event/archive/Release-12.zip
)
CPMAddPackage(
NAME J3ML
URL https://git.redacted.cc/josh/j3ml/archive/Release-3.1.zip
)
CPMAddPackage(
NAME jlog
URL https://git.redacted.cc/josh/jlog/archive/Prerelease-12.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-13.zip
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-32.zip
)
CPMAddPackage(
NAME JGL
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-32.zip
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-50.zip
)
target_include_directories(JUI PUBLIC ${Event_SOURCE_DIR}/include)
@@ -65,7 +70,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)

View File

@@ -2,31 +2,58 @@
![Static Badge](https://img.shields.io/badge/Lit-Based-%20)
Fourth Time's The Charm! (tm)
Fourth Time's The Charm!
JUI is a C++20 Library for building interactive menus in OpenGL / Redacted3D.
#### JUI is a C++20 Library for building interactive menus in OpenGL / Redacted3D.
It is expressly built with our Redacted3D engine in mind, but steps have been taken to support OpenGL generally.
![JUI First Showcase](showcase.png)
## Abstract
JUI provides a set of objects that we term Widgets. Widgets can be styled and laid out on-screen in relation to each other. Each widget has a single parent, and a list of child elements. Your root widget should be a Scene object.
Provided widgets include Scene, Rect, Text, TextRect, Button, TextButton, TextInputForms, Slider, Image, ImageRect, RadioButton
JUI provides a set of objects that we term Widgets, which can be styled and laid out on-screen in relation to each other. Each widget has a single parent, and a list of child elements. Your root widget should be a Scene object.
## Features
### Why use this instead of imgui?
## Usage
* Comprehensive list of common UI widgets:
* ![Scene](https://git.redacted.cc/josh/ReJUI/wiki/Scene)
* ![Rect](https://git.redacted.cc/josh/ReJUI/wiki/Rect)
* ![Text](https://git.redacted.cc/josh/ReJUI/wiki/Text)
* ![TextRect](https://git.redacted.cc/josh/ReJUI/wiki/TextRect)
* ![Button](https://git.redacted.cc/josh/ReJUI/wiki/Button)
* ![TextButton](https://git.redacted.cc/josh/ReJUI/wiki/TextButton)
* ![TextInputForm](https://git.redacted.cc/josh/ReJUI/wiki/TextInputForm)
* ![Slider](https://git.redacted.cc/josh/ReJUI/wiki/Slider)
* ![Image](https://git.redacted.cc/josh/ReJUI/wiki/Image)
* ![ImageRect](https://git.redacted.cc/josh/ReJUI/wiki/ImageRect)
* ![RadioButton](https://git.redacted.cc/josh/ReJUI/wiki/RadioButton)
* ![Window](https://git.redacted.cc/josh/ReJUI/wiki/Window)
* ![Checkbox](https://git.redacted.cc/josh/ReJUI/wiki/Checkbox)
* ![NineSliceRect](https://git.redacted.cc/josh/ReJUI/wiki/NineSliceRect)
* 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
![JUI First Showcase](showcase.png)
Browse the src/Demos directories for examples of building and interacting with things in JUI.
Browse the Repository's ![Wiki](https://git.redacted.cc/josh/ReJUI/wiki), linked above, for further explainations and sample code on each widget.
## Dependencies
ReJUI shares dependencies with it's rendering layer, ![JGL](https://git.redacted.cc/josh/JGL).
Currently, the package is also integrated with ![J3ML](https://git.redacted.cc/josh/J3ML) and ![ReWindow](https://git.redacted.cc/Redacted/ReWindow).
`Fedora/RHEL: dnf install cmake make gcc-g++ libX11 libX11-devel mesa-libGL-devel vulkan-loader-devel`
`Ubuntu/Debian: apt-get install cmake make gcc g++ libx11-6 libx11-dev libgl-dev libvulkan-dev libxrandr-dev`
## Documentation
Documentation is automatically generated from latest commit and is hosted at https://doc.redacted.cc/jui .
@@ -35,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

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

View File

@@ -19,19 +19,30 @@ using J3ML::LinearAlgebra::Vector2;
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
class ImageBase
{
public:
/// The default constructor initializes this ImageBase with a null pointer texture.
ImageBase();
ImageBase(JGL::Texture* texture);
/// This constructor initializes this ImageBase with a JGL::Texture pointer.
explicit ImageBase(JGL::Texture* texture);
public:
[[nodiscard]] JGL::Texture* Content() const { return texture;}
[[nodiscard]] Color4 Color() const { return image_color;}
[[nodiscard]] Vector2 Scale() const { return scale;}
[[nodiscard]] Vector2 Origin() const { return origin;}
/// Returns the texture pointer held by this object.
[[nodiscard]] JGL::Texture* Content() const;
/// Returns the color of this object. The texture is rendered with this color.
[[nodiscard]] Color4 Color() const;
/// Returns the scale factor of this object. The texture is scaled with this value.
[[nodiscard]] Vector2 Scale() const;
/// Returns the origin factor of this object. The texture's origin is determined by this value.
[[nodiscard]] Vector2 Origin() const;
void SetContent(JGL::Texture* content);
/// Sets the texture of this object.
void Content(JGL::Texture* content);
/// Sets the color of this object.
void Color(const Color4& newColor);
/// Sets the scale factor of this object.
void Scale(const Vector2& newScale);
void Origin(const Vector2& newOrigin);
public:

View File

@@ -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>

View File

@@ -13,8 +13,9 @@
#include <J3ML/LinearAlgebra.hpp>
#include <Event.h>
#include <EventConnection.h>
#include <Color4.hpp>
#include "JGL/types/RenderTarget.h"
namespace JUI
{
@@ -36,12 +37,16 @@ namespace JUI
//Event<Vector2> MousePress;
//Event<Vector2, bool> MouseRelease;
public:
void SetCornerRounding(float radius);
void SetCornerRounding(float tlRadius, float trRadius, float blRadius, float brRadius);
void SetCornerRoundingTL(float radius);
void SetCornerRoundingTR(float radius);
void SetCornerRoundingBL(float radius);
void SetCornerRoundingBR(float radius);
void CornerRounding(float radius);
float CornerRounding() const;
// TODO: Implement per-corner rounding in JGL::Outline/FillRect
//void CornerRounding(float tlRadius, float trRadius, float blRadius, float brRadius);
//void CornerRoundingTL(float radius);
//void CornerRoundingTR(float radius);
//void CornerRoundingBL(float radius);
//void CornerRoundingBR(float radius);
void SetClipsDescendants(bool clipping);
void BGColor(const Color4& col);
@@ -52,21 +57,19 @@ namespace JUI
Color4 BGColor() const;
Color4 GetBorderColor() const;
float GetBorderWidth() const;
BorderMode GetBorderMode() const;
enum BorderMode BorderMode() const;
void SetBorderMode(const BorderMode& mode)
{
border_mode = mode;
}
void BorderMode(const enum BorderMode& mode);
bool GetClipsDescendants() const;
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:
BorderMode border_mode = BorderMode::Middle;
enum BorderMode border_mode = BorderMode::Middle;
bool mouse_press_debounce;
bool mouse_inside_debounce;
float border_width = 1.f;

View File

@@ -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, uint 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})) {};
};

View File

@@ -7,36 +7,32 @@
/// @file Widget.hpp
/// @desc Base Widget Class - All JUI widgets extend their behavior from this class.
/// @edit 2024-07-31
/// @edit 2024-10-11
#pragma once
#include <Event.h>
#include <EventConnection.h>
#include <string>
#include <vector>
#include "J3ML/LinearAlgebra.hpp"
#include "JUI/UDim2.hpp"
#include <J3ML/LinearAlgebra.hpp>
#include <JUI/UDim2.hpp>
#include <Color3.hpp>
#include <Color4.hpp>
#include <JGL/JGL.h>
#include <ReWindow/types/Key.h>
#include <JUI/Tween.hpp>
#include <JUI/JUI.hpp>
using namespace JGL;
namespace JUI
{
enum class MouseButton {
Left = 1,
Middle = 2,
Right = 3
};
namespace JUI {
using namespace J3ML::Math;
using namespace J3ML::LinearAlgebra;
/// Widget class is the base for all JUI elements.
/// Widgets exist in a tree hierarchy where each object has one parent, and an arbitrary number of children.
/// 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
{
public:
@@ -66,16 +62,76 @@ namespace JUI
Event<Widget *> Destroying;
public:
void TweenPositionTo(const UDim2& goal)
{
UDim2 start_position = this->Position();
float progress = 0;
Tween t;
std::function<void(float)> updateTillGoalReached = [this, t, start_position, progress, goal] (float elapsed) mutable
{
UDim2 current_pos = this->Position();
// TODO: Implement UDim equality operators (with epsilon)
// TODO: Implement UDim::Lerp
// TODO: Implement UDim2::Lerp
if (current_pos.X.Pixels == goal.X.Pixels && current_pos.Y.Pixels == goal.Y.Pixels && current_pos.X.Scale == goal.X.Scale && current_pos.Y.Scale == goal.Y.Scale)
{
// Reached Goal, Complete this tween.
return;
}
progress += elapsed;
if (progress >= 1.f)
{
progress = 1.f;
t.Completed.Invoke();
return;
}
float modified_progress = EasingFunctions::EaseOutElastic(progress);
UDim2 pos = current_pos;
pos.X.Pixels = Math::Lerp(start_position.X.Pixels, goal.X.Pixels, modified_progress);
pos.Y.Pixels = Math::Lerp(start_position.Y.Pixels, goal.Y.Pixels, modified_progress);
pos.X.Scale = Math::Lerp(start_position.X.Scale, goal.X.Scale, modified_progress);
pos.Y.Scale = Math::Lerp(start_position.Y.Scale, goal.Y.Scale, modified_progress);
Position(pos);
};
t.tick_func = updateTillGoalReached;
tweens.push_back(t);
}
void TweenPositionFromTo();
void TweenSizeTo();
void TweenSizeFromTo();
/// Adds a given widget to this widget's list of children.
/// @return The widget in question.
Widget* Add(Widget* newChild);
/// Returns true if this object is an ancestor to the given widget.
bool IsAncestorOf(Widget* descendant) const;
/// Returns true if this object is a descendant of the given widget.
bool IsDescendantOf(Widget* ancestor) const;
/// Returns the first child widget that has the given symbolic name, Otherwise, a nullpointer is returned
/// TODO: Use std::optional here and anywhere else a nullptr could be returned.
Widget* FindFirstChild(const std::string& name);
/// Returns the first child widget that has the given symbolic name
std::optional<Widget*> FindFirstChild(const std::string& name);
/// Returns a flat list of all nodes that are lower in the hierarchy list.
std::vector<Widget*> GetDescendants();
@@ -90,16 +146,16 @@ namespace JUI
[[nodiscard]] virtual Vector2 GetAbsolutePosition() const;
[[nodiscard]] virtual float GetAbsoluteRotation() const;
/// Returns the parent of this widget
/// TODO: Use std::optional here and anywhere else a nullptr could be returned.
/// Returns the parent of this widget, or a nullptr if there is no parent.
/// @see GetFamilyTreeRoot().
Widget* GetParent() const;
/// 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;
/// Retrieves this widgets z-index. This value determines the order in which a widget
/// renders to the screen relative to other Widgets.
/// Returns the Z-Index of this widget.
/// This value determines the order in which a widget renders to the screen relative to other Widgets.
/// @note This applies in ascending priority order,
/// meaning lower values are rendered first, placing higher values on top of lower values.
/// @note The range of valid values is -MAX_INT to MAX_INT.
@@ -109,23 +165,23 @@ namespace JUI
/// Sets this widgets z-index.
/// @see ZIndex().
void SetZIndex(int zindex);
void ZIndex(int);
/// Returns the menu-coordinates that are used to size this widget in relation to its parent.
/// @see SetSize(), class UDim2
/// @see Size(), class UDim2
[[nodiscard]] UDim2 Size() const;
/// Sets the widgets pixel and scalar position using a combined data type.
/// This position is centered around the object's anchor point.
/// @see Position(), SetAnchorPoint(), GetAnchorPoint(), class UDim2.
void SetPosition(const UDim2 &pos);
/// @see Position(), AnchorPoint(), AnchorPoint(), class UDim2.
void Position(const UDim2&);
/// Sets this widget's pixel and scalar size using a combined data type.
/// @see Size(), class UDim2.
void SetSize(const UDim2 &s);
void Size(const UDim2&);
/// Sets the parent object of this widget. Positioning and sizing of a widget is relative to it's parent.
void SetParent(Widget*);
void Parent(Widget*);
/// Returns true if this widget is a 'descendant' of the specified potential ancestor. Otherwise returns false.
bool IsDescendantOf(Widget *ancestor);
@@ -134,93 +190,136 @@ namespace JUI
bool IsAncestorOf(Widget *descendant);
/// Determines the origin point of a Widget, relative to it's absolute size.
[[nodiscard]] Vector2 GetAnchorPoint() const;
/// TODO: Better explain what this allows for.
[[nodiscard]] Vector2 AnchorPoint() const;
void AnchorPoint(const Vector2 &point);
/// Returns the padding factor on the left of this widget.
/// @see SetPaddingLeft(), class UDim.
[[nodiscard]] UDim GetPaddingLeft() const;
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @see PaddingLeft(), class UDim.
[[nodiscard]] UDim PaddingLeft() const;
/// Returns the padding factor on the top of this widget.
/// @see SetPaddingTop(), class UDim.
[[nodiscard]] UDim GetPaddingTop() const;
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @see PaddingTop(), class UDim.
[[nodiscard]] UDim PaddingTop() const;
/// Returns the padding factor on the right of this widget.
/// @see SetPaddingRight(), class UDim.
[[nodiscard]] UDim GetPaddingRight() const;
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @see PaddingRight(), class UDim.
[[nodiscard]] UDim PaddingRight() const;
/// Returns the padding factor on the bottom of this widget.
/// @see SetPaddingBottom(), class UDim.
[[nodiscard]] UDim GetPaddingBottom() const;
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @see PaddingBottom(), class UDim.
[[nodiscard]] UDim PaddingBottom() const;
void SetPaddingLeft(const UDim &pad_left);
void SetPaddingTop(const UDim &pad_top);
void SetPaddingRight(const UDim &pad_right);
void SetPaddingBottom(const UDim &pad_bottom);
/// Sets 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.
void PaddingLeft(const UDim &pad_left);
/// Sets the padding factor on the top of this widget.
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @see PaddingLeft(), class UDim.
void PaddingTop(const UDim &pad_top);
/// Sets the padding factor on the right of this widget.
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
/// @see PaddingLeft(), class UDim.
void PaddingRight(const UDim &pad_right);
/// Sets the padding factor on the bottom of this widget.
/// 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
/// @param top
/// @param right
/// @param bottom
void Padding(const UDim& left, const UDim& top, const UDim& right, const UDim& bottom);
void SetPadding(const UDim& left, const UDim& top, const UDim& right, const UDim& bottom);
void SetPadding(const UDim& padding);
/// Sets the same padding factor on all four sides of this widget.
/// Padding refers to spacing on the inside of elements, while margin is spacing outside the element.
void Padding(const UDim& padding);
/// Returns the margin factor on the left of this widget.
[[nodiscard]] UDim GetMarginLeft() const;
[[nodiscard]] UDim MarginLeft() const;
/// Returns the margin factor on the top of this widget.
[[nodiscard]] UDim GetMarginTop() const;
[[nodiscard]] UDim MarginTop() const;
/// Returns the margin factor on the right of this widget.
[[nodiscard]] UDim GetMarginRight() const;
[[nodiscard]] UDim MarginRight() const;
/// Returns the margin factor on the bottom of this widget.
[[nodiscard]] UDim GetMarginBottom() const;
[[nodiscard]] UDim MarginBottom() const;
/// Sets the amount (in Pixels + Scale) to apply margin on the left-side of the widget.
/// @see UDim, SetMargin()
void SetMarginLeft(const UDim &ml);
/// @see UDim, Margin()
void MarginLeft(const UDim &ml);
/// Sets the amount (in Pixels + Scale) to apply margin on the top-side of the widget.
/// @see UDim, SetMargin()
void SetMarginTop(const UDim &mt);
/// @see UDim, Margin()
void MarginTop(const UDim &mt);
/// Sets the amount (in Pixels + Scale) to apply margin on the right-side of the widget.
/// @see UDim, SetMargin()
void SetMarginRight(const UDim &mr);
/// @see UDim, Margin()
void MarginRight(const UDim &mr);
/// Sets the amount (in Pixels + Scale) to apply margin on the bottom-side of the widget.
/// @see UDim, SetMargin()
void SetMarginBottom(const UDim &mb);
/// @see UDim, Margin()
void MarginBottom(const UDim &mb);
/// Sets the margin factor of each side of the widget.
/// @param left The amount of margin to apply on the left. (Pixels, Scale)
/// @param top The amount of margin to apply on the top.
/// @param right The amount of margin to apply on the right.
/// @param bottom The amount of margin to apply on the bottom.
void SetMargin(const UDim& left, const UDim& top, const UDim& right, const UDim& bottom);
void Margin(const UDim& left, const UDim& top, const UDim& right, const UDim& bottom);
/// Sets the margin factor on all four sides of this widget to the same value.
/// @see SetMargin(const UDim&, const UDim&, const UDim&, const UDim&).
void SetMargin(const UDim& margin);
/// @see Margin(const UDim&, const UDim&, const UDim&, const UDim&).
void Margin(const UDim& margin);
/// 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 GetName().
[[nodiscard]] std::string GetName() const;
/// @see Name().
[[nodiscard]] std::string Name() const;
/// Sets this widgets mnemonic name.
/// Widgets can optionally be assigned a name that can be used to retrieve it from a widget tree node.
/// @see GetName().
void SetName(const std::string &new_name);
/// @see Name().
void Name(const std::string &new_name);
/// Returns whether the widget is to be rendered.
/// This function does not indicate whether the widget is **actually seeable** on-screen.
/// @see SetVisible().
/// @see Visible().
[[nodiscard]] bool IsVisible() const;
/// Sets whether the widget is to be rendered. Children are also not rendered if disabled.
/// @see IsVisible().
void SetVisible(bool enabled);
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 SetAnchorPoint(const Vector2 &point);
/// 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);
public:
// TODO: Consider calling J2D::Begin here.
virtual void PreDraw() {}
// TODO: Consider calling J2D::End here.
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();
@@ -237,20 +336,27 @@ namespace JUI
/// 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);
/// 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 void ObserveKeyInput(Key key, bool pressed);
protected:
void DrawChildWidgets();
void UpdateChildWidgets(float delta);
protected:
MouseButton mbtn;
bool mb_state;
bool prev_mb_state;
bool mb_state = false;
bool prev_mb_state = false;
//int last_known_mouse_button;
//bool last_known_mouse_button_state;
Vector2 last_known_mouse_pos;
UDim2 position;
UDim2 size;
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;
@@ -266,97 +372,25 @@ namespace JUI
bool visible = true;
Widget* next = nullptr;
Widget* prev = nullptr;
int zindex = 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);
};
/// A mixin helper class that provides behavior for hoverable objects.
/// A hoverable object pays attention to when the mouse enters and leaves it's bounds.
class Hoverable {
public:
Event<Vector2> OnHoverEvent;
Event<Vector2> OnExitEvent;
public:
bool IsHovered() const { return hovered; };
public:
virtual void OnHover(const Vector2& MousePos) {
OnHoverEvent.Invoke(MousePos);
};
virtual void OnExit(const Vector2& MousePos) {
OnExitEvent.Invoke(MousePos);
};
void Update(const Vector2& m_pos, float delta)
{
if (IsHovered() && !hover_debounce) {
OnHover(m_pos);
hover_debounce = true;
}
if (!IsHovered() && hover_debounce) {
OnExit(m_pos);
hover_debounce = false;
}
}
protected:
bool hovered;
bool hover_debounce;
};
/// A mixin helper class that provides behavior and events for clickable widgets.
class Clickable
{
public:
Event<Vector2, MouseButton> OnClickEvent;
Event<Vector2, MouseButton, bool> OnReleaseEvent;
public:
bool IsClicked() const { return clicked; };
void SetClicked(bool manual_click)
{
clicked = manual_click;
}
public:
virtual void OnClick(const Vector2& MousePos, const MouseButton& MouseButton) {
OnClickEvent.Invoke(MousePos, MouseButton);
};
virtual void OnRelease(const Vector2& MousePos, const MouseButton& MouseButton, bool MouseStillOver) {
OnReleaseEvent.Invoke(MousePos, MouseButton, MouseStillOver);
};
void Update(const Vector2& mpos, const MouseButton& btn, bool hovering)
{
}
protected:
bool clicked = false;
bool click_debounce = false;
};
/// A mixin helper class that provides behavior and events for a binary-state togglable widget.
class Toggleable {
public:
Event<> OnToggleEvent;
Event<> OnToggleOnEvent;
Event<> OnToggleOffEvent;
public:
virtual void OnToggleOn() {
OnToggleOnEvent.Invoke();
}
virtual void OnToggleOff() {
OnToggleOffEvent.Invoke();
}
virtual void Update() {
OnToggleEvent.Invoke();
toggled = !toggled;
if (toggled) {
OnToggleOn();
} else {
OnToggleOff();
}
}
protected:
bool toggled = false;
};
}

View File

@@ -1,8 +1,16 @@
//
// Created by dawsh on 7/10/24.
//
#pragma once
#ifndef JUI_HPP
#define JUI_HPP
#include <jlog/Logger.hpp>
#endif //JUI_HPP
namespace JUI
{
extern jlog::GenericLogger 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
};
}

View File

@@ -0,0 +1,38 @@
/// 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 ScrollingRect.hpp
/// @desc Scrolling Rectangle Widget
/// @edit 2024-10-11
#pragma once
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <JUI/Base/Widget.hpp>
namespace JUI
{
/// A mixin helper class that provides behavior and events for clickable widgets.
class Clickable
{
public:
Event<Vector2, MouseButton> OnClickEvent;
Event<Vector2, MouseButton, bool> OnReleaseEvent;
public:
[[nodiscard]] bool IsClicked() const { return clicked; };
void SetClicked(bool manual_click);
public:
virtual void OnClick(const Vector2& MousePos, const MouseButton& MouseButton);;
virtual void OnRelease(const Vector2& MousePos, const MouseButton& MouseButton, bool MouseStillOver);;
void Update(const Vector2& mpos, const MouseButton& btn, bool hovering);
protected:
bool clicked = false;
bool click_debounce = false;
};
}

View File

@@ -0,0 +1,18 @@
/// 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 ScrollingRect.hpp
/// @desc Scrolling Rectangle Widget
/// @edit 2024-10-11
#pragma once
namespace JUI
{
/// A mixin helper class that enables a widget to be docked into a DockingStation.
class Dockable {};
}

View File

@@ -0,0 +1,40 @@
/// 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 ScrollingRect.hpp
/// @desc Scrolling Rectangle Widget
/// @edit 2024-10-11
#pragma once
#include <Event.h>
#include <J3ML/LinearAlgebra/Vector2.hpp>
namespace JUI
{
/// A mixin helper class that enables a widget to be dragged around by the mouse.
class Draggable {
public:
Event<> OnDragBegan;
Event<> OnDragEnded;
bool Dragging() const { return dragging;}
virtual void SetDrag(bool b) {
this->dragging = b;
}
virtual void StartDragging(const Vector2& point)
{
initial_drag_offset = point;
}
virtual void StopDragging() {}
virtual void Update(float delta) {}
protected:
bool dragging = false;
Vector2 initial_drag_offset = {0, 0};
};
}

View File

@@ -0,0 +1,42 @@
/// 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 Hoverable.hpp
/// @desc Hoverable Mixin Helper Class - Added to widgets that should have special behavior when hovered by the mouse.
/// @edit 2024-10-11
#pragma once
#include <J3ML/LinearAlgebra/Vector2.hpp>
#include <Event.h>
#include <JUI/Widgets/Tooltip.hpp>
namespace JUI
{
/// A mixin helper class that provides behavior for hoverable objects.
/// A hoverable object pays attention to when the mouse enters and leaves it's bounds.
class Hoverable {
public:
Event<Vector2> OnHoverEvent;
Event<Vector2> OnExitEvent;
public:
bool IsHovered() const { return hovered; };
public:
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:
//Tooltip* tooltip = nullptr;
//float tooltip_threshold = 0.f;
//float tooltip_limit = 0.125f;
bool hovered = false;
bool hover_debounce = false;
};
}

View File

@@ -0,0 +1,34 @@
/// Josh's User Interface Library
/// A C++20 Library for creating, styling, and rendering of a UI/UX widgets.
/// Developed and Maintained by Josh O'Leary @ Redacted Software.
/// Special Thanks to William Tomasine II and Maxine Hayes.
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Resizable.hpp
/// @desc Resizable Mixin Helper class
/// @edit 2024-10-11
#pragma once
#include <Event.h>
#include <J3ML/LinearAlgebra/Vector2.hpp>
namespace JUI
{
/// A mixin helper class that enables a widget to be resized by the mouse.
class Resizable {
public:
Event<> OnDragBegan;
Event<> OnDragEnded;
[[nodiscard]] bool Resizing() const;
virtual void SetResize(bool b);
virtual void StartResizing(const Vector2& point);
virtual void StopResizing();
virtual void Update(float delta);
protected:
bool resizing = false;
Vector2 initial_resize_offset = {0, 0};
};
}

View File

@@ -0,0 +1,32 @@
/// 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>
namespace JUI
{
/// A mixin helper class that provides behavior and events for a binary-state togglable widget.
class Toggleable {
public:
Event<> OnToggleEvent;
Event<> OnToggleOnEvent;
Event<> OnToggleOffEvent;
public:
virtual void OnToggleOn();
virtual void OnToggleOff();
virtual void Update();
protected:
bool toggled = false;
};
}

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

@@ -0,0 +1,114 @@
#pragma once
#include <functional>
#include "Event.h"
namespace JUI
{
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);
}
/// A class that represents an animation-in-action.
class Tween {
public:
Event<> Completed;
void Update(float elapsed)
{
}
std::function<float(float)> easing_func;
std::function<void(float)> tick_func;
/// Duration of the tween, in seconds.
float time;
/// Time of delay until the tween begins, in seconds.
float delay_time;
/// Number of times the tween repeats. -1 indicates indefinite repetition.
int repeat_count;
/// Whether or not the tween interpolates in reverse once the initial tween completes.
bool reverses;
float lifetime = 5;
float progress = 0;
bool alive = true;
bool paused = false;
bool completed = false;
void Pause() { paused = true; }
void Resume() { paused = false; }
bool Paused() const { return paused; }
bool HasCompleted() const { return completed; }
};
}

View File

@@ -11,13 +11,14 @@
#pragma once
#include "JUI/Base/Widget.hpp"
#include "JUI/Base/RectBase.hpp"
#include "JUI/Base/TextBase.hpp"
#include <JUI/Base/Widget.hpp>
#include <JUI/Base/RectBase.hpp>
#include <JUI/Base/TextBase.hpp>
#include <JUI/Mixins/Clickable.hpp>
#include <JUI/Mixins/Hoverable.hpp>
namespace JUI
{
enum class ButtonState
{
Base,
@@ -26,49 +27,98 @@ namespace JUI
Disabled
};
//class Button : public Widget, public RectBase, public Clickable, public Hoverable
class Button : public Widget, public RectBase, public Clickable, public Hoverable
/// 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 Rect, public Clickable, public Hoverable
{
public:
Event<> OnEnabled;
Event<> OnDisabled;
public:
Button();
explicit Button(Widget* parent);
~Button() override {};
public:
// TODO: These suffice for now as a proof-of-concept
// But I want more sophisticated style-animation support
// for the various states of the button
Color4 GetHoveredBGColor() const;
Color4 GetBaseBGColor() const;
Color4 GetPressedBGColor() const;
Color4 GetDisabledBGColor() const;
Color4 GetHoveredBorderColor() const;
Color4 GetBaseBorderColor() const;
Color4 GetPressedBorderColor() const;
Color4 GetDisabledBorderColor() const;
/// Returns the background color of this object when the mouse is hovering this button.
/// @note Setting BGColor and BorderColor directly on this class is not recommended, as it will be overwritten.
[[nodiscard]] Color4 HoveredBGColor() const;
/// Returns the background color of this object at rest.
/// @note Setting BGColor and BorderColor directly on this class is not recommended, as it will be overwritten.
[[nodiscard]] Color4 BaseBGColor() const;
/// Returns the background color of this object when it is clicked.
/// @note Setting BGColor and BorderColor directly on this class is not recommended, as it will be overwritten.
[[nodiscard]] Color4 PressedBGColor() const;
/// Returns the background color of this object when it is disabled.
/// @note Setting BGColor and BorderColor directly on this class is not recommended, as it will be overwritten.
[[nodiscard]] Color4 DisabledBGColor() const;
/// Returns the border color of this object when it is hovered.
/// @note Setting BGColor and BorderColor directly on this class is not recommended, as it will be overwritten.
[[nodiscard]] Color4 HoveredBorderColor() const;
/// Returns the border color of this object at rest.
/// @note Setting BGColor and BorderColor directly on this class is not recommended, as it will be overwritten.
[[nodiscard]] Color4 BaseBorderColor() const;
/// Returns the border color of this object when it is clicked.
/// @note Setting BGColor and BorderColor directly on this class is not recommended, as it will be overwritten.
[[nodiscard]] Color4 PressedBorderColor() const;
/// Returns the border color of this object when it is disabled.
/// @note Setting BGColor and BorderColor directly on this class is not recommended, as it will be overwritten.
[[nodiscard]] Color4 DisabledBorderColor() const;
bool IsEnabled() const;
/// Sets the background color of this object when the mouse is hovering.
void HoveredBGColor(const Color4& color);
/// Sets the background color of this object at rest.
void BaseBGColor(const Color4& color);
/// Sets the background color of this object when it is clicked.
void PressedBGColor(const Color4& color);
/// Sets the background color of this object when it is disabled.
void DisabledBGColor(const Color4& color);
/// Sets the border color of this object when the mouse is hovering.
void HoveredBorderColor(const Color4& color);
/// Sets the border color of this object at rest.
void BaseBorderColor(const Color4& color);
/// Sets the border color of this object when it is clicked.
void PressedBorderColor(const Color4& color);
/// 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.
bool Disabled() const;
/// Sets the interactable state of this button.
void SetEnabled(bool enabled);
void Draw() override;
/// Disables this button. Mouse events will be ignored and the button will grey-out.
void Disable();
/// Enables this button. @see Disable, SetEnabled
void Enable();
void Update(float delta) override;
void GotoHoverColor();
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 = Colors::Blues::SkyBlue;
Color4 hover_border = Colors::Blues::DarkSlateBlue;
Color4 pressed_bg = Colors::Blues::DarkSlateBlue;
Color4 pressed_border = Colors::Blues::SkyBlue;;
Color4 disabled_bg = Colors::Gray;
Color4 disabled_border = Colors::Gray;
Color4 base_bg = Colors::White;
Color4 base_border = Colors::Black;
void UpdateVisualState();
};
class TextButton : public Button, public TextBase {
public:
TextButton();
explicit TextButton(Widget* parent);
~TextButton() override {};
void Update(float delta) override;
void Draw() override;
};
}

View File

@@ -1,8 +0,0 @@
//
// Created by dawsh on 8/1/24.
//
#ifndef JUI_CANVAS_HPP
#define JUI_CANVAS_HPP
#endif //JUI_CANVAS_HPP

View File

@@ -1,8 +1,104 @@
//
// Created by dawsh on 8/5/24.
//
/// 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.
#ifndef JUI_CHECKBOX_HPP
#define JUI_CHECKBOX_HPP
/// @file Checkbox.hpp
/// @desc It's a checkbox.
/// @edit 2024-10-28
#endif //JUI_CHECKBOX_HPP
#pragma once
#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"
namespace JUI
{
class Checkbox : public Button {
public:
Checkbox() : Button() { }
explicit Checkbox(Widget *parent) : Checkbox()
{
this->Parent(parent);
}
void Update(float delta) override
{
Button::Update(delta);
}
void OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &bnt, bool still_hovering) override
{
Button::OnRelease(mouse_pos, bnt, still_hovering);
checked = !checked;
}
void InnerDraw() override
{
Rect::InnerDraw();
if (checked)
{
//J2D::Begin();
Vector2 check_padding = {2, 2};
RectBase::Draw(check_color, check_color, GetAbsolutePosition()+check_padding, GetAbsoluteSize()-(check_padding*2));
//J2D::End();
}
}
void Draw() override
{
if (!visible)
return;
//J2D::Begin();
Vector2 abs_pos = GetAbsolutePosition();
Vector2 abs_size = GetAbsoluteSize();
auto root_size = GetFamilyTreeRoot()->GetAbsoluteSize();
GLint *old_scissor_bounds;
bool clip_was_enabled;
if (clips_descendants) {
clip_was_enabled = glIsEnabled(GL_SCISSOR_TEST);
if (clip_was_enabled)
glGetIntegerv(GL_SCISSOR_BOX, old_scissor_bounds);
float presumed_screen_height = 600;
glScissor(abs_pos.x, presumed_screen_height-abs_size.y-abs_pos.y, abs_size.x, abs_size.y);
glEnable(GL_SCISSOR_TEST);
}
RectBase::Draw(abs_pos, abs_size);
// Draw Child Elements with scissor clipping still active
Widget::Draw();
// Returns clip to previous state
if (clips_descendants)
{
//glScissor(old_scissor_bounds[0], old_scissor_bounds[1], old_scissor_bounds[2], old_scissor_bounds[3]);
if (!clip_was_enabled) {}
glDisable(GL_SCISSOR_TEST);
}
//J2D::End();
}
protected:
bool checked;
Color4 check_color = Colors::Red;
private:
};
}

View File

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

View File

@@ -1,8 +0,0 @@
//
// Created by dawsh on 8/1/24.
//
#ifndef JUI_CONTEXTMENU_HPP
#define JUI_CONTEXTMENU_HPP
#endif //JUI_CONTEXTMENU_HPP

View File

@@ -0,0 +1,19 @@
#pragma once
namespace JUI
{
/// DialogWindow is a special case of Window which is intended to be opened over all other widgets, and present a message or option to the user.
class DialogWindow
{
};
/// Sample cases:
/// Single Option Dialog
/// Multi Option Dialog
/// File Picker Dialog
/// Font Chooser Dialog
/// Text Entry Dialog
}

View File

@@ -15,9 +15,41 @@
namespace JUI
{
// TODO: TableLayout class?
/// A GridLayout lays out sibling UI elements in multiple rows within the parent UI element.
class GridLayout : public LayoutContainer
{
public:
GridLayout() : LayoutContainer() {
}
explicit GridLayout(Widget* parent) : GridLayout()
{
this->Parent(parent);
}
~GridLayout() override {}
void ApplyLayout() override
{
int consumed_w;
int consumed_h;
int cells;
// TODO: Implement widget.LayoutOrder and sort by that number.
for (auto& child_widget : children)
{
}
}
protected:
UDim2 cell_padding;
UDim2 cell_size;
int max_cells;
bool start_from_left;
private:
};
}

View File

@@ -7,7 +7,7 @@
/// @file Image.hpp
/// @desc A widget that contains and renders an image provided by JGL.
/// @edit 2024-08-05
/// @edit 2024-10-31
#pragma once
@@ -16,11 +16,21 @@
namespace JUI
{
/// The image class contains and renders an image provided by JGL.
/// By default, this class renders it's image to fill the parent widget.
/// For a self-contained image rectangle, see ImageRect.hpp
class Image : public Widget, public ImageBase
{
public:
/// The default constructor initializes the image with a nullptr texture.
Image();
/// Constructs a new image by explicitly setting the parent Widget.
explicit Image(Widget* parent);
/// Constructs a new image by explicitly setting both the parent Widget and the texture.
/// @param parent
/// @param tex
explicit Image(Widget* parent, JGL::Texture* tex);
~Image() override {}

View File

@@ -0,0 +1,17 @@
#include <JUI/Base/ImageBase.hpp>
#include <JUI/Widgets/Button.hpp>
namespace JUI {
class ImageButton;
}
class JUI::ImageButton : public ImageBase, public Button {
public:
void Update(float delta) override;
void Draw() override;
public:
ImageButton() : ImageBase(), Button() {}
explicit ImageButton(Widget* parent) : ImageButton() { Parent(parent); }
public:
~ImageButton() override = default;
};

View File

@@ -0,0 +1,25 @@
/// 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 ImageRect.hpp
/// @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
{
};
}

View File

@@ -0,0 +1,8 @@
//
// Created by dawsh on 11/24/24.
//
#ifndef LISTBOX_HPP
#define LISTBOX_HPP
#endif //LISTBOX_HPP

View File

@@ -9,7 +9,6 @@
/// @desc Clickable Button widget.
/// @edit 2024-08-02
#pragma once
#include <JUI/Base/LayoutContainer.hpp>
@@ -30,60 +29,31 @@ namespace JUI
enum class V { TOP, BOTTOM };
}
/// Lays child elements out in a vertical descending list.
/// Child element positions are overriden by this widget.
/// Lays child elements out in a vertical list.
/// Child element positions are overridden by this widget.
class VerticalListLayout : public ListLayout
{
public:
VerticalListLayout();
explicit VerticalListLayout(Widget* parent);
void ApplyLayout() override
{
int consumed_height = 0;
for (auto& child_widget : children)
{
// TODO: Implement widget.LayoutOrder property
// TODO: Sort children by LayoutOrder
consumed_height += pad_top.Pixels;
child_widget->SetPosition({0, consumed_height, 0, 0});
consumed_height += child_widget->GetAbsoluteSize().y;
consumed_height += pad_bottom.Pixels;
}
}
void ApplyLayout() override;
void LayoutOrder(LayoutOrder::V order);
[[nodiscard]] LayoutOrder::V LayoutOrder() const;
protected:
LayoutOrder::V layout = LayoutOrder::V::TOP;
};
/// Lays child elements out in a horizontal list.
/// Child element positions are overridden by this widget.
class HorizontalListLayout : public ListLayout
{
public:
HorizontalListLayout();
explicit HorizontalListLayout(Widget* parent);
void ApplyLayout() override
{
if (layout == LayoutOrder::H::LEFT)
{
int consumed_width = 0;
for (auto &child_widget : children)
{
consumed_width += pad_left.Pixels;
child_widget->SetPosition({consumed_width, 0, 0, 0});
consumed_width += child_widget->GetAbsoluteSize().x;
consumed_width += pad_right.Pixels;
}
}
if (layout == LayoutOrder::H::RIGHT)
{
int consumed_width = this->GetAbsoluteSize().x;
for (auto &child_widget : children)
{
consumed_width -= pad_left.Pixels;
child_widget->SetPosition({consumed_width, 0, 0, 0});
consumed_width -= child_widget->GetAbsoluteSize().x;
consumed_width -= pad_right.Pixels;
}
}
}
void ApplyLayout() override;
void LayoutOrder(LayoutOrder::H order);
[[nodiscard]] LayoutOrder::H LayoutOrder() const;
protected:
LayoutOrder::H layout = LayoutOrder::H::LEFT;
};

View File

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

View File

@@ -1,8 +0,0 @@
//
// Created by dawsh on 8/1/24.
//
#ifndef JUI_NINESLICEIMAGE_HPP
#define JUI_NINESLICEIMAGE_HPP
#endif //JUI_NINESLICEIMAGE_HPP

View File

@@ -1,8 +1,7 @@
//
// Created by dawsh on 8/1/24.
//
#pragma once
#ifndef JUI_PROGRESSBAR_HPP
#define JUI_PROGRESSBAR_HPP
#endif //JUI_PROGRESSBAR_HPP
namespace JUI
{
}

View File

@@ -11,6 +11,7 @@
#include <JUI/Widgets/Button.hpp>
#include <JUI/Mixins/Toggleable.hpp>
namespace JUI
{

View File

@@ -13,6 +13,7 @@
#include <JUI/Base/Widget.hpp>
#include <JUI/Base/RectBase.hpp>
#include <J3ML/Geometry/AABB2D.hpp>
namespace JUI {
@@ -27,10 +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;
};
}

View File

@@ -1,15 +1,25 @@
/// 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 Scene.hpp
/// @desc Scene class - The root class of a menu that contains child elements.
/// @edit 2024-10-11
#pragma once
#include <JUI/Base/Widget.hpp>
namespace JUI
{
// TODO: SceneSizingBehavior - FillToWindow, CustomSized
///
class Scene : public Widget {
public:
Scene();
~Scene() override {}
void Draw() override;
void SetViewportSize(int w, int h);
Event<Vector2> MouseMoved;
@@ -20,7 +30,5 @@ namespace JUI
[[nodiscard]] Vector2 GetAbsoluteSize() const override;
protected:
int viewport_w;
int viewport_h;
};
}

View File

@@ -1,8 +1,72 @@
//
// Created by dawsh on 8/1/24.
//
/// 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.
#ifndef JUI_SCROLLINGRECT_HPP
#define JUI_SCROLLINGRECT_HPP
/// @file ScrollingRect.hpp
/// @desc Scrolling Rectangle Widget
/// @edit 2024-10-11
#endif //JUI_SCROLLINGRECT_HPP
#pragma once
#include <JUI/Widgets/Rect.hpp>
namespace JUI {
class ScrollingRect;
}
/// 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 {
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;
JGL::RenderTarget* canvas = nullptr;
protected:
/* This isn't public because nothing should ever
* have to do this from the outside. -Redacted */
void RecomputeRenderTarget();
public:
float scroll_size = 0;
JGL::RenderTarget* GetCanvas();
[[nodiscard]] Vector2i CanvasSize() const;
[[nodiscard]] float ScrollPos() const { return scroll; }
[[nodiscard]] Vector2 CanvasPosition() const;
// TODO scrolling in either direction. Assuming vertical scroll for now.
Vector2 CanvasAbsolutePosition() const;
Vector2 CanvasAbsoluteSize() const;
public:
void ScrollPos(float pos) { scroll = pos; }
void CanvasSize(const Vector2i& new_size);
void InnerDraw() override;
void Draw() override;
void Update(float delta) override
{
//scroll += delta*5;
//canvas->Resize(Vector2i(GetAbsoluteSize().x, GetAbsoluteSize().y));
Rect::Update(delta);
}
void ObserveKeyInput(Key key, bool pressed) override
{
if (key == Keys::UpArrow && pressed)
{
scroll -= 10;
}
if (key == Keys::DownArrow && pressed)
{
scroll += 10;
}
}
public:
~ScrollingRect() override;
ScrollingRect();
explicit ScrollingRect(Widget* parent);;
};

View File

@@ -5,6 +5,67 @@
/// (c) 2024 Redacted Software
/// This work is dedicated to the public domain.
/// @file Widget.hpp
/// @desc Base Widget Class - All JUI widgets extend their behavior from this class.
/// @edit 2024-07-31
/// @file Slider.hpp
/// @desc Slider widget class header.
/// @edit 2024-10-31
#pragma once
#include "Button.hpp"
#include "Rect.hpp"
#include "JUI/Mixins/Draggable.hpp"
namespace JUI
{
template<typename T>
T roundMultiple( T value, T multiple )
{
if (multiple == 0) return value;
return static_cast<T>(std::round(static_cast<double>(value)/static_cast<double>(multiple))*static_cast<double>(multiple));
}
/// A slider is a widget with a handle which can be pulled back and forth to change the value.
class Slider : public Rect, public Clickable, public Hoverable
{
public:
Event<float> ValueChanged;
Slider() = default;
explicit Slider(JUI::Widget* parent);
[[nodiscard]] float Minimum() const;
[[nodiscard]] float Maximum() const;
[[nodiscard]] float Interval() const;
[[nodiscard]] float CurrentValue() const;
[[nodiscard]] Color4 ScrubberColor() const;
[[nodiscard]] float ScrubberWidth() const;
[[nodiscard]] float Range() const;
void Minimum(float min);
void Maximum(float max);
void Interval(float inter);
void CurrentValue(float value);
void ScrubberColor(const Color4& color);
void ScrubberWidth(float width);
void OnClick(const J3ML::LinearAlgebra::Vector2 &MousePos, const JUI::MouseButton &MouseButton) override;
void OnRelease(const J3ML::LinearAlgebra::Vector2 &MousePos, const JUI::MouseButton &MouseButton, bool MouseStillOver) override;
void OnHover(const J3ML::LinearAlgebra::Vector2 &MousePos) override;
void OnExit(const J3ML::LinearAlgebra::Vector2 &MousePos) override;
void Update(float delta) override;
void Draw() override;
protected:
float minimum = 0;
float maximum = 1;
float interval = 0.1;
float current;
bool dragging;
float scrubber_width = 20;
Color4 scrubber_color = Colors::White;
private:
};
}

View File

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

View File

@@ -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:

View File

@@ -1,9 +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 TextButton.hpp
/// @desc Button class with text. @see Button, Text
/// @edit 2024-10-31
#pragma once
#include <JUI/Widgets/Button.hpp>
namespace JUI
{
class TextButton : public Button, public TextBase
{
/// Text-displaying button widget.
class TextButton : public Button, public TextBase {
public:
TextButton();
explicit TextButton(Widget* parent);
~TextButton() override {};
void Update(float delta) override;
void Draw() override;
};
}

View File

@@ -9,10 +9,64 @@
/// @desc A box that accepts user keyboard input.
/// @edit 2024-08-02
#pragma once
#include <JUI/Mixins/Clickable.hpp>
#include "TextRect.hpp"
namespace JUI
{
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 ObserveKeyInput(Key key, bool pressed) override;
void ObserveMouseInput(MouseButton btn, bool pressed) override;
void 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);
// TODO: Implement selection of part of input text.
Color4 GetSelectionColor() const;
void SetSelectionColor(const Color4& color);
bool HasSelection() const;
std::string GetSelectedText() const;
bool HasFocus() const;
void SetFocused(bool focused);
void GrabFocus();
void DropFocus();
std::vector<char> GetBlacklist() const { return blacklist;}
protected:
bool clear_text_on_return;
bool focused = false;
bool selection_enabled;
bool selection_active;
int selection_start_index;
int selection_end_index;
int cursor_position = 0;
std::string input_buffer;
float cursor_blink_time = 0.f;
Color4 autocomplete_color = Colors::Black;
std::string autocomplete_text = "Hello World";
bool hide_autocomplete_on_select = true;
bool autocomplete_text_enabled = true;
std::vector<char> blacklist;
private:
};
}

View File

@@ -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;
};
}

View File

@@ -0,0 +1,44 @@
#pragma once
#include <JUI/Widgets/TextRect.hpp>
#include "ReWindow/InputService.h"
namespace JUI
{
class Tooltip : public TextRect
{
public:
Tooltip() : TextRect()
{
}
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:
};
}

View File

@@ -0,0 +1,106 @@
#pragma once
namespace JUI
{
class ContextMenu : public Rect {
public:
ContextMenu() : Rect() {
this->BGColor(Colors::White);
this->Margin(2_px);
this->Size({200, 200, 0, 0});
layout = new VerticalListLayout(this);
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->SetBorderWidth(2);
this->Margin(2_px);
this->BorderMode(BorderMode::Outline);
layout = new HorizontalListLayout(this);
//layout->PaddingLeft(2_px);
layout->PaddingRight(2_px);
//layout->PaddingRight(2_px);
}
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->SetBorderWidth(0.f);
btn->SetContent(name);
return btn;
}
void SetFont(const JGL::Font& use_my_font)
{
font = use_my_font;
}
protected:
HorizontalListLayout* layout;
JGL::Font font;
private:
};
}

View File

@@ -1,6 +1,14 @@
//
// Created by dawsh on 8/1/24.
//
/// 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 Viewport.hpp
/// @desc A rectangular window that contains a 'viewport' to a
/// @edit 2024-10-31
#ifndef JUI_VIEWPORT_HPP
#define JUI_VIEWPORT_HPP

View File

@@ -13,106 +13,101 @@
#include <J3ML/LinearAlgebra.hpp>
#include <JUI/Base/Widget.hpp>
#include <JUI/Base/RectBase.hpp>
#include <JUI/Widgets/Rect.hpp>
#include <JUI/Widgets/Text.hpp>
#include <JUI/Widgets/Button.hpp>
#include <JUI/Mixins/Draggable.hpp>
#include <JUI/Mixins/Resizable.hpp>
#include <JUI/Mixins/Dockable.hpp>
#include <JUI/Mixins/Hoverable.hpp>
#include <JUI/Mixins/Clickable.hpp>
#include <JUI/Widgets/TextButton.hpp>
#include <JUI/Widgets/ImageButton.hpp>
namespace JUI
{
using J3ML::LinearAlgebra::Vector2;
/// A mixin helper class that enables a widget to be dragged around by the mouse.
class Draggable {
public:
Event<> OnDragBegan;
Event<> OnDragEnded;
bool Dragging() const { return dragging;}
virtual void SetDrag(bool b) {
this->dragging = b;
}
virtual void StartDragging(const Vector2& point)
{
initial_drag_offset = point;
}
virtual void StopDragging() {}
virtual void Update(float delta) {}
protected:
bool dragging = false;
Vector2 initial_drag_offset = {0, 0};
};
/// A mixin helper class that enables a widget to be resized by the mouse.
class Resizable {
public:
Event<> OnDragBegan;
Event<> OnDragEnded;
bool Resizing() const { return resizing;}
virtual void SetResize(bool b) {
this->resizing = b;
}
virtual void StartResizing(const Vector2& point)
{
initial_resize_offset = point;
}
virtual void StopResizing() {}
virtual void Update(float delta) {}
protected:
bool resizing = false;
Vector2 initial_resize_offset = {0, 0};
};
/// A mixin helper class that enables a widget to be docked into a DockingStation.
class Dockable {};
/// 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, 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);
/// Returns the minimum size (in x,y pixels) that the Window widget is allowed to be.
[[nodiscard]] Vector2 MinSize() const;
/// Returns the maximum size (in x,y pixels) that the Window widget is allowed to be.
[[nodiscard]] Vector2 MaxSize() const;
/// Returns the current size (in x,y pixels) of the Window widget.
[[nodiscard]] Vector2 CurrentSize() const;
/// Returns the minimum size (in x,y pixels) that the Window widget is allowed to be.
[[nodiscard]] Vector2 MinSize() const;
/// Sets the minimum size (in x,y pixels) that the Window widget is allowed to be.
void MinSize(const Vector2& constraint);
/// Returns the maximum size (in x,y pixels) that the Window widget is allowed to be.
[[nodiscard]] Vector2 MaxSize() const;
/// 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;
/// Returns the text displayed as the Window's title.
[[nodiscard]] std::string Title() const;
void SetTitle(const std::string& title);
/// Returns whether dragging the window about with the mouse is enabled.
/// @see class Draggable.
bool IsDraggable() const;
void SetDraggable(bool value);
/// Returns whether this Window is able to be 'Docked' into another widget.
/// @see class Dockable.
bool IsDockable() const;
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;
/// Returns a pointer to the Text Widget that is used to render the title bar's text.
Text* GetTitleInstance();
/// Returns a pointer to the Rect Widget that is used to layout the title bar contents.
Rect* GetTopbarInstance();
/// Returns a pointer to the Rect Widget that is used to layout the contents of the window.
Rect* GetViewportInstance();
/// Returns a pointer to the Exit Button Widget.
TextButton* GetExitButtonInstance();
void SetResizable(bool value);
/// Returns a pointer to the Text Widget that is used to render the title bar's text.
Text* TitleInstance();
/// Returns a pointer to the Rect Widget that is used to layout the title bar contents.
Rect* TopbarInstance();
/// Returns a pointer to the Rect Widget that is used to layout the contents of the window.
Rect* ViewportInstance();
/// Returns a pointer to the Exit Button Widget.
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;
@@ -128,17 +123,17 @@ namespace JUI
JUI::Rect* Topbar;
JUI::Rect* Viewport;
JUI::Text* TitleLabel;
JUI::TextButton* exit_btn;
JUI::TextButton* fs_btn;
JUI::ImageButton* exit_btn;
JUI::ImageButton* fs_btn;
std::string title = "JUI Window";
bool resizable = true;
//bool resizing = false;
bool draggable = true;
bool dockable = false;
int titlebar_height = 16;
int titlebar_height = 20;
int title_font_size = 16;
Vector2 max_size;
Vector2 min_size; //= {30, 30};
UDim2 size_when_restart_began;
};
}

365
main.cpp
View File

@@ -19,186 +19,216 @@
#include <JUI/Widgets/ListLayout.hpp>
#include <JUI/Widgets/TextRect.hpp>
#include <JUI/Widgets/Image.hpp>
#include <rewindow/types/window.h>
#include <jlog/Logger.hpp>
#include <JUI/Widgets/Slider.hpp>
#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"
JGL::Font FreeSans;
JUI::Scene* scene;
JGL::Texture* sample_texture;
JGL::Texture* slicer;
JUI::VerticalListLayout* list;
JUI::ScrollingRect* scroller;
JUI::Scene* CreateScene() {
using namespace JUI;
Scene *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->SetAnchorPoint(Vector2(0.5f, 0.5f));
JUI->SetPosition({50_percent, 50_percent});
//JUI->SetSize({30_percent, 10_percent});
JUI->FitText(true);
JUI->SetPadding(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->SetPosition({50_percent, 60_percent});
//Redacted->SetSize({30_percent, 10_percent});
Redacted->AlignCenterHorizontally();
Redacted->AlignTop();
Redacted->SetAnchorPoint(Vector2(0.5f, .5f));
Redacted->FitText(true);
Redacted->SetPadding(10_px);
Redacted->BGColor({32, 48, 192});
Redacted->SetBorderWidth(1);
Redacted->BorderColor({64, 64, 64});
auto* nineslice_demo_window = new JUI::Window(root);
nineslice_demo_window->CornerRounding(5);
nineslice_demo_window->Size({50_percent, 50_percent});
nineslice_demo_window->SetTitle("9-Slice Demo");
nineslice_demo_window->Visible(false);
// Rect //
Rect *rect_element = new Rect(root);
rect_element->SetName("JimBob");
auto* topbar = new UtilityBar(root);
topbar->ZIndex(3);
auto* file = topbar->AddButton("Demos");
//Rect* element = new Rect(root);
auto* file_tt = new JUI::Tooltip(file);
file_tt->SetContent("Tooltip");
//rect_element->SetName("JimBob");
//element->BGColor({0,255,0});
rect_element->BGColor({0, 64, 0});
rect_element->SetSize({0, 0, 0.1f, 0.2f});
rect_element->SetClipsDescendants(true);
rect_element->BorderColor({255, 255, 255});
rect_element->SetBorderWidth(2.f);
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");
rect_element->MouseEnter += [rect_element](auto coords) {
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("");
ctx_menu->ZIndex(3);
rect_element->BGColor({0, 128, 0});
};
rect_element->MouseExit += [rect_element](auto coords) {
rect_element->BGColor({0, 64, 0});
};
topbar->AddButton("Edit");
auto* view = topbar->AddButton("View");
// End Rect //
topbar->AddButton("Help");
//auto* horizontal = new HorizontalListLayout(root);
//horizontal->ZIndex(1);
auto* sizer_1 = new Rect(root);
//sizer_1->ZIndex(4);
sizer_1->Size({0,-24,0.2f, 1.f});
sizer_1->Position({0, 24, 0, 0});
sizer_1->BGColor(Colors::Grays::Gainsboro);
auto* s1_vert = new VerticalListLayout(sizer_1);
auto* button = new TextButton(s1_vert);
//button->Position({5, 105, 0, 0});
button->Size({0, 35, 1, 0});
button->SetTextColor(Colors::Black);
button->SetContent("Button");
button->AlignLeft();
button->Padding(5_px);
auto* tt2 = new JUI::Tooltip(button);
tt2->SetContent("Test 123");
auto* button2 = new TextButton(s1_vert);
//button2->Position({5, 105, 0, 0});
button2->Size({0, 35, 1, 0});
button2->SetTextColor(Colors::Black);
button2->SetContent("Button");
button2->AlignCenterHorizontally();
auto* button3 = new TextButton(s1_vert);
//button2->Position({5, 105, 0, 0});
button3->Size({0, 35, 1, 0});
button3->SetTextColor(Colors::Black);
button3->SetContent("Button");
button3->AlignRight();
auto* checkbox_container = new Rect(s1_vert);
checkbox_container->Size({0, 35, 1, 0});
// Button //
RadioButton *button_element = new RadioButton(root);
button_element->SetName("BobJim");
button_element->BGColor({0, 0, 64});
button_element->SetSize({0, 0, 0.1f, 0.1f});
button_element->SetClipsDescendants(true);
button_element->BorderColor({255, 255, 255});
button_element->SetBorderWidth(2.f);
auto bpos = rect_element->Size();
auto* checkbox_horiz = new HorizontalListLayout(checkbox_container);
//exit(1);
button_element->SetPosition(
{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* check1 = new Checkbox(checkbox_horiz);
// Window //
JUI::Window* win_element = new JUI::Window(root);
win_element->SetTitleFont(FreeSans);
win_element->SetSize({50_percent, 50_percent});
win_element->SetTitle("JUI Example Window Widget");
//win_element->SetPadding(1_px);
auto* input_form = new TextInputForm(s1_vert);
input_form->Size({0,30, 1, 0});
input_form->SetContent("");
input_form->SetTextSize(14);
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");
other_window->TweenPositionTo({50_percent, 50_percent});
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 list = new VerticalListLayout(win_element->GetViewportInstance());
list->SetPadding(10_px);
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(nineslice_demo_window->ViewportInstance());
list->Padding(10_px);
TextRect* a = new TextRect(list);
a->SetTextSize(16);
a->SetFont(FreeSans);
a->SetSize({0, 20, 1, 0});
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.");
//b->FitText(true);
b->SetSize({0, 20, 1, 0});
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->SetSize({0, 20, 1, 0});
c->Center();
c->SetFont(FreeSans);
TextRect* d = new TextRect(list);
d->SetTextSize(16);
d->SetContent("Exit");
//d->FitText(true);
d->SetSize({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});
auto darkie = new Image(win_element->GetViewportInstance(), sample_texture);
darkie->FitImageToParent(true);
darkie->Color({255,255,255,128});
root->SetViewportSize(800, 600);
root->SetViewportSize({800, 600});
return root;
}
class JUIDevelopmentTestWindow : public ReWindow::RWindow {
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);
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->SetContent(std::format("{} Sampled Delta: {}ms", -iter, Math::Floor(elapsed*1000.f)));
scroller->scroll_size += 20;
}
}
void Draw()
@@ -206,17 +236,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();
}
@@ -226,40 +258,69 @@ public:
return true;
}
//bool OnResizeRequest(const ReWindow::WindowResizeRequestEvent &e) override {}
JUIDevelopmentTestWindow() : ReWindow::RWindow() {}
};
void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent &) override
{
}
void OnMouseButtonDown(const ReWindow::MouseButtonDownEvent &) override
{
}
void OnMouseMove(const ReWindow::MouseMoveEvent &) override
{
}
void OnKeyDown(const ReWindow::KeyDownEvent &) override
{
}
//bool OnResizeRequest(const ReWindow::WindowResizeRequestEvent &e) override {}
//JUIDevelopmentTestWindow() : ReWindow::OpenGLWindow() {}
};
int main()
{
for (float i = 0; i < 1; i += 0.01f)
{
std::cout << JUI::EasingFunctions::EaseInSine(i) << std::endl;
}
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();
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);
scene->ObserveMouseMovement(Vector2(e.Position.x, e.Position.y));
};
window->OnMouseButtonUpEvent += [&] (MouseButtonUpEvent e) {
@@ -283,13 +344,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;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 136 KiB

View File

@@ -6,15 +6,15 @@ using namespace JGL;
namespace JUI
{
void ImageBase::Draw(const Vector2 &pos, const Vector2 &size) {
J2D::Begin();
//J2D::Begin();
// TODO: Support image rotation in the widget.
J2D::DrawSprite(*texture, pos, 0, origin, scale, image_color);
J2D::End();
//J2D::End();
}
ImageBase::ImageBase()
{
this->texture = nullptr;
}
ImageBase::ImageBase(JGL::Texture* tex) : ImageBase()
@@ -22,7 +22,7 @@ namespace JUI
this->texture = tex;
}
void ImageBase::SetContent(JGL::Texture *content) {
void ImageBase::Content(JGL::Texture *content) {
this->texture = content;
}
@@ -31,5 +31,13 @@ namespace JUI
void ImageBase::Scale(const Vector2 &newScale) { scale = newScale;}
void ImageBase::Origin(const Vector2 &newOrigin) { origin = newOrigin; }
JGL::Texture *ImageBase::Content() const { return texture;}
Color4 ImageBase::Color() const { return image_color;}
Vector2 ImageBase::Scale() const { return scale;}
Vector2 ImageBase::Origin() const { return origin;}
}

View File

@@ -5,7 +5,7 @@ namespace JUI
LayoutContainer::LayoutContainer() {}
LayoutContainer::LayoutContainer(Widget* parent) {
this->SetParent(parent);
this->Parent(parent);
}
void LayoutContainer::Update(float delta)

View File

@@ -27,22 +27,61 @@ namespace JUI {
SetBorderWidth(width);
}
void RectBase::Draw(const Vector2 &abs_pos, const Vector2 &abs_size) {
// Background rect
J2D::Begin();
J2D::FillRect(bg_color, abs_pos, abs_size);
// Outline rect
Vector2 border_offset = {border_width/2, border_width/2};
// TODO: implement border_mode behavior when rendering here.
if (corner_rounding_radius >= 0)
J2D::OutlineRect(border_color, abs_pos - border_offset, abs_size + (border_offset*2), border_width);
J2D::End();
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) {
//Draw(nullptr, bgColor, borderColor, abs_pos, abs_size);
//}
void RectBase::Draw(const Color4& bgColor, const Color4& borderColor, const Vector2 &abs_pos, const Vector2 &abs_size) {
//J2D::Begin(canvas, false);
// Background rect
if (corner_rounding_radius > 0)
J2D::FillRoundedRect(bgColor, abs_pos, abs_size, corner_rounding_radius);
else
J2D::FillRect(bgColor, abs_pos, abs_size);
// Outline rect - compute the size change to fit the border accurately.
Vector2 border_offset = {0, 0};
if (border_mode == BorderMode::Inset)
border_offset = {-border_width/2.f, -border_width/2.f};
if (border_mode == BorderMode::Middle)
border_offset = {0, 0};
if (border_mode == BorderMode::Outline)
border_offset = {border_width/2.f, border_width/2.f};
// Draw the outline.
if (border_width > 0)
{
if (corner_rounding_radius > 0)
J2D::OutlineRoundedRect(borderColor, abs_pos - border_offset, abs_size + (border_offset*2), corner_rounding_radius, border_width);
else
J2D::OutlineRect(borderColor, abs_pos - border_offset, abs_size + (border_offset*2), border_width);
}
//J2D::End();
}
void RectBase::BorderMode(const enum BorderMode &mode) {
border_mode = mode;
}
BorderMode RectBase::BorderMode() const { return border_mode; }
void RectBase::CornerRounding(float radius) {
corner_rounding_radius = radius;
}
float RectBase::CornerRounding() const { return corner_rounding_radius; }
}

View File

@@ -1,124 +1,129 @@
#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, uint 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;
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) {}

View File

@@ -1,6 +1,6 @@
#include "JUI/Base/Widget.hpp"
#include <JUI/Base/Widget.hpp>
#include <jlog/Logger.hpp>
#include <J3ML/Geometry/AABB2D.hpp>
namespace JUI {
@@ -11,26 +11,26 @@ namespace JUI {
Widget::Widget(Widget* parent) : Widget()
{
this->SetParent(parent);
this->Parent(parent);
}
void Widget::SetParent(Widget* 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 (parent == oldParent)
if (newParent == oldParent)
return;
// Don't allow for an instance to be parented to itself
if (this == parent)
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(parent))
if (this->IsAncestorOf(newParent))
throw std::runtime_error("Cannot create circular reference.");
for (Widget* ancestor: this->GetAncestors()) {
if (oldParent && !ancestor->IsAncestorOf(parent) && parent != ancestor) {
if (oldParent && !ancestor->IsAncestorOf(newParent) && newParent != ancestor) {
ancestor->DescendantRemoved(this);
for (Widget* descendant : this->GetDescendants()) {
ancestor->DescendantRemoved(descendant);
@@ -41,21 +41,21 @@ namespace JUI {
// Remove ourselves from our parent (if we have one)
if (this->parent) {
// this->parent->ChildRemoved(this)
std::erase(parent->children, this);
std::erase(this->parent->children, this);
}
// Update our old parent to the new one
this->parent = parent;
this->parent = newParent;
// If our parent is set to nullptr, we can't update it's vector of children
if (!parent) return;
if (!newParent) return;
// Add ourselves to our new parent's list of children
parent->children.emplace_back(this);
newParent->children.emplace_back(this);
//newParent->ChildAdded(this);
for (Widget* ancestor : this->GetAncestors()) {
if (!oldParent || (!oldParent->IsDescendantOf(parent) && oldParent != ancestor)) {
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()) {
@@ -88,65 +88,68 @@ namespace JUI {
return false;
}
Vector2 Widget::GetAnchorPoint() const {
Vector2 Widget::AnchorPoint() const {
return anchor_point;
}
UDim Widget::GetPaddingLeft() const { return pad_left;}
UDim Widget::GetPaddingTop() const { return pad_top;}
UDim Widget::GetPaddingRight() const { return pad_right;}
UDim Widget::GetPaddingBottom() const { return 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::SetPaddingLeft (const UDim& pl) { pad_left = pl;}
void Widget::SetPaddingTop (const UDim& pt) { pad_top = pt;}
void Widget::SetPaddingRight (const UDim& pr) { pad_right = pr;}
void Widget::SetPaddingBottom(const UDim& pb) { pad_bottom = pb;}
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; }
void Widget::SetPadding(const UDim &left, const UDim &top, const UDim &right, const UDim &bottom) {
SetPaddingLeft(left);
SetPaddingTop(top);
SetPaddingRight(right);
SetPaddingBottom(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::SetPadding(const UDim &padding) {
SetPadding(padding, padding, padding, padding);
void Widget::Padding(const UDim &padding) {
Padding(padding, padding, padding, padding);
}
UDim Widget::GetMarginLeft () const { return margin_left;}
UDim Widget::GetMarginTop () const { return margin_top;}
UDim Widget::GetMarginRight () const { return margin_right;}
UDim Widget::GetMarginBottom() const { return margin_bottom;}
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::SetMarginLeft(const UDim& ml) { margin_left = ml;}
void Widget::SetMarginTop(const UDim& mt) { margin_top = mt;}
void Widget::SetMarginRight(const UDim& mr) { margin_right = mr;}
void Widget::SetMarginBottom(const UDim& mb) { margin_bottom = mb;}
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::SetMargin(const UDim &left, const UDim &top, const UDim &right, const UDim &bottom) {
SetMarginLeft(left);
SetMarginTop(top);
SetMarginRight(right);
SetMarginBottom(bottom);
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::SetMargin(const UDim &margin) {
SetMargin(margin, margin, margin, margin);
void Widget::Margin(const UDim &margin) {
Margin(margin, margin, margin, margin);
}
std::string Widget::GetName() const { return name; }
void Widget::SetName(const std::string& new_name) { name = new_name;}
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::SetVisible(bool enabled) { visible = enabled;}
void Widget::Visible(bool enabled) { visible = enabled;}
Widget* Widget::GetParent() const { return parent;}
Widget* Widget::GetParent() const {
return parent;
}
UDim2 Widget::Position() const { return position; }
void Widget::SetPosition(const UDim2& pos) { position = pos; }
void Widget::Position(const UDim2& pos) { position = pos; }
UDim2 Widget::Size() const { return size;}
void Widget::SetSize(const UDim2& s) { size = s; }
void Widget::Size(const UDim2& s) { size = s; }
float Widget::GetAbsoluteRotation() const {
// TODO: implement rotation correctly!!
@@ -174,9 +177,59 @@ namespace JUI {
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::GetAbsolutePaddingTopLeft() const {
auto parent_abs_size = this->GetParent()->GetAbsoluteSize();
UDim padding_h = parent->PaddingLeft();
UDim padding_v = parent->PaddingTop();
float padding_x = padding_h.Pixels + (padding_h.Scale * parent_abs_size.x);
float padding_y = padding_v.Pixels + (padding_v.Scale * parent_abs_size.y);
return {padding_x, padding_y};
}
Vector2 Widget::GetAbsoluteMarginTopLeft()
{
// TODO: Implement correctly.
return {0,0};
}
Vector2 Widget::GetAbsolutePaddingBottomRight() const {
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 padding = {final_pad_x, final_pad_y};
return padding;
}
Vector2 Widget::GetAbsoluteMarginBottomRight()
{
// TODO: Implement correctly.
return {0,0};
}
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();
@@ -184,16 +237,9 @@ namespace JUI {
auto abs_size = GetAbsoluteSize();
auto anchor_offset = abs_size * GetAnchorPoint();
UDim padding_h = parent->GetPaddingLeft();
UDim padding_v = parent->GetPaddingTop();
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);
Vector2 padding_offset = {calculated_padding_x, calculated_padding_y};
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;
@@ -201,35 +247,30 @@ namespace JUI {
return absolute_position - anchor_offset;
}
// TODO: implement padding shrinking abs_size
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();
UDim padding_h = parent->GetPaddingLeft() + parent->GetPaddingRight();
float final_pad_x = padding_h.Pixels + (padding_h.Scale * parent->GetAbsoluteSize().x);
UDim padding_v = parent->GetPaddingTop() + parent->GetPaddingBottom();
float final_pad_y = padding_v.Pixels + (padding_v.Scale * parent->GetAbsoluteSize().y);
Vector2 pad_size_reduction = {final_pad_x, final_pad_y};
Vector2 pad_size_reduction = GetAbsolutePaddingBottomRight();
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;
}
// TODO: Consider std::optional
Widget* Widget::FindFirstChild(const std::string& search_name) {
std::optional<Widget*> Widget::FindFirstChild(const std::string& search_name) {
for (auto& child : children) {
if (child->GetName() == search_name)
if (child->Name() == search_name)
return child;
}
return nullptr;
return std::nullopt;
}
@@ -237,15 +278,33 @@ namespace JUI {
if (!visible)
return;
PreDraw();
InnerDraw();
PostDraw();
DrawChildWidgets();
}
void Widget::UpdateTweens(float elapsed)
{
for (Tween& t: tweens)
{
t.tick(elapsed);
}
}
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();
}
@@ -297,6 +356,13 @@ namespace JUI {
}
}
void Widget::ObserveKeyInput(Key key, bool pressed) {
for (Widget* child : children) {
child->ObserveKeyInput(key, pressed);
}
}
bool Widget::IsAncestorOf(Widget *descendant) const {
if (descendant == nullptr)
return false;
@@ -322,12 +388,33 @@ namespace JUI {
}
Widget *Widget::Add(Widget *newChild) {
newChild->SetParent(this);
newChild->Parent(this);
return newChild;
}
void Widget::SetAnchorPoint(const Vector2& point) {
void Widget::AnchorPoint(const Vector2& point) {
anchor_point = point;
}
int Widget::ZIndex() const { return 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);
}
}
AABB2D Widget::AbsoluteBounds() const {
return {GetAbsolutePosition(), GetAbsoluteSize()};
}
}

View File

@@ -1,3 +1,9 @@
//
// Created by dawsh on 7/10/24.
//
#include <JUI/JUI.hpp>
namespace JUI
{
std::ofstream GlobalLogFile("latest.log", std::ios_base::app);
jlog::GenericLogger UILogs {"JUI", GlobalLogFile, Colors::Green, Colors::Gray, Colors::Gray, Colors::Green, Colors::White};
}

View File

@@ -0,0 +1,20 @@
#include <JUI/Mixins/Clickable.hpp>
void JUI::Clickable::Update(const Vector2 &mpos, const JUI::MouseButton &btn, bool hovering) {
}
void JUI::Clickable::OnRelease(const Vector2 &MousePos, const JUI::MouseButton &MouseButton, bool MouseStillOver) {
clicked = false;
OnReleaseEvent.Invoke(MousePos, MouseButton, MouseStillOver);
}
void JUI::Clickable::OnClick(const Vector2 &MousePos, const JUI::MouseButton &MouseButton) {
clicked = true;
OnClickEvent.Invoke(MousePos, MouseButton);
}
void JUI::Clickable::SetClicked(bool manual_click) {
clicked = manual_click;
}

View File

@@ -0,0 +1 @@
#include <JUI/Mixins/Dockable.hpp>

View File

@@ -0,0 +1 @@
#include <JUI/Mixins/Draggable.hpp>

View File

@@ -0,0 +1,37 @@
#include <JUI/Mixins/Hoverable.hpp>
void JUI::Hoverable::OnHover(const Vector2 &MousePos) {
OnHoverEvent.Invoke(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;
}
if (!IsHovered() && hover_debounce) {
OnExit(m_pos);
hover_debounce = false;
}
}

View File

@@ -0,0 +1,15 @@
#include <JUI/Mixins/Resizable.hpp>
void JUI::Resizable::SetResize(bool b) {
this->resizing = b;
}
void JUI::Resizable::StartResizing(const Vector2 &point) {
initial_resize_offset = point;
}
void JUI::Resizable::StopResizing() {}
void JUI::Resizable::Update(float delta) {}
bool JUI::Resizable::Resizing() const { return resizing; }

View File

@@ -0,0 +1,20 @@
#include <JUI/Mixins/Toggleable.hpp>
void JUI::Toggleable::OnToggleOn() {
OnToggleOnEvent.Invoke();
}
void JUI::Toggleable::OnToggleOff() {
OnToggleOffEvent.Invoke();
}
void JUI::Toggleable::Update() {
OnToggleEvent.Invoke();
toggled = !toggled;
if (toggled) {
OnToggleOn();
} else {
OnToggleOff();
}
}

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

@@ -0,0 +1,151 @@
#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;}
}

View File

@@ -4,64 +4,54 @@
namespace JUI
{
Button::Button(): Widget() {}
Button::Button(Widget *parent): Clickable() {
this->SetParent(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;
// 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;
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();
Button::Button(): Rect(), Clickable() {
BGColor(BaseBGColor());
BorderColor(BaseBorderColor());
}
Button::Button(Widget *parent): Button() {
this->Parent(parent);
};
void Button::OnClick(const Vector2& mouse_pos, const MouseButton& btn)
{
if (disabled)
return;
Clickable::OnClick(mouse_pos, btn);
clicked = true;
//BGColor(PressedBGColor());
//BorderColor(PressedBorderColor());
}
void Button::OnRelease(const J3ML::LinearAlgebra::Vector2 &mouse_pos, const JUI::MouseButton &btn,
bool still_hovering) {
if (disabled)
return;
Clickable::OnRelease(mouse_pos, btn, still_hovering);
clicked = false;
//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) {
@@ -81,23 +71,124 @@ namespace JUI
OnRelease(last_known_mouse_pos, mbtn, IsHovered());
}
UpdateVisualState();
prev_mb_state = mb_state;
Rect::Update(delta);
}
TextButton::TextButton() : Button(), TextBase() {}
TextButton::TextButton(Widget* parent) : TextButton() {
this->SetParent(parent);
void Button::Enable() {
SetEnabled(true);
}
void TextButton::Update(float delta) {
Button::Update(delta);
TextBase::Update(delta);
void Button::Disable() {
SetEnabled(false);
}
void TextButton::Draw() {
Button::Draw();
TextBase::Draw(this->GetAbsolutePosition(), this->GetAbsoluteSize());
bool Button::Enabled() const {
return !disabled;
}
bool Button::Disabled() const {
return disabled;
}
void Button::SetEnabled(bool enabled) {
// if state actually changed
if (disabled != enabled)
return;
if (disabled)
OnDisabled.Invoke();
else
OnEnabled.Invoke();
disabled = !enabled;
BGColor(DisabledBGColor());
BorderColor(DisabledBorderColor());
}
void Button::OnHover(const Vector2 &MousePos) {
Hoverable::OnHover(MousePos);
if (Disabled() || IsClicked())
return;
//BGColor(HoveredBGColor());
//BorderColor(HoveredBorderColor());
}
void Button::OnExit(const Vector2 &MousePos) {
Hoverable::OnExit(MousePos);
if (Disabled() || IsClicked())
return;
//BGColor(BaseBGColor());
//BorderColor(BaseBorderColor());
}
Color4 Button::HoveredBGColor() const { return hover_bg; }
Color4 Button::BaseBGColor() const { return base_bg; }
Color4 Button::PressedBGColor() const { return pressed_bg; }
Color4 Button::DisabledBGColor() const { return disabled_bg;}
Color4 Button::HoveredBorderColor() const { return hover_border;}
Color4 Button::BaseBorderColor() const { return base_border;}
Color4 Button::PressedBorderColor() const { return pressed_border;}
Color4 Button::DisabledBorderColor() const { return disabled_border;}
void Button::HoveredBGColor(const Color4 &color) { hover_bg = color;}
void Button::BaseBGColor(const Color4 &color) { base_bg = color;}
void Button::PressedBGColor(const Color4 &color) { pressed_bg = color;}
void Button::DisabledBGColor(const Color4 &color) { disabled_bg = color;}
void Button::HoveredBorderColor(const Color4 &color) { hover_border = color;}
void Button::BaseBorderColor(const Color4 &color) {base_border = color; }
void Button::PressedBorderColor(const Color4 &color) { pressed_border = color;}
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();
}
//void Button::SetTooltip(const std::string &content, float delay) {
//tooltip_limit = delay;
// TODO: Verify this is okay to do:
//tooltip = new Tooltip(this);
//tooltip->SetContent(content);
//tooltip->ZIndex(5);
//tooltip->Visible(false);
//}
}

View File

@@ -0,0 +1,8 @@
#include <JUI/Widgets/Checkbox.hpp>
namespace JUI
{
}

View File

@@ -9,12 +9,12 @@ namespace JUI
Image::Image(Widget* parent) : Image()
{
SetParent(parent);
Parent(parent);
}
Image::Image(Widget *parent, JGL::Texture *tex) : Widget(), ImageBase(tex)
{
this->SetParent(parent);
this->Parent(parent);
}
Vector2 Image::GetAbsoluteSize() const {
@@ -31,7 +31,7 @@ namespace JUI
if (fit_image_to_parent)
{
auto old_scale = scale;
scale = GetAbsoluteSize() / texture->GetDimensions();
scale = GetAbsoluteSize() / Vector2(texture->GetDimensions());
ImageBase::Draw(GetAbsolutePosition(), GetAbsoluteSize());
scale = old_scale;
} else

View File

@@ -0,0 +1,10 @@
#include <JUI/Widgets/ImageButton.hpp>
void JUI::ImageButton::Update(float delta) {
Button::Update(delta);
}
void JUI::ImageButton::Draw() {
Button::Draw();
ImageBase::Draw(GetAbsolutePosition()+GetAbsolutePaddingTopLeft(), GetAbsoluteSize()-GetAbsolutePaddingBottomRight());
}

View File

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

View File

@@ -8,13 +8,86 @@ namespace JUI
VerticalListLayout::VerticalListLayout() : ListLayout() {}
VerticalListLayout::VerticalListLayout(Widget* parent) : VerticalListLayout()
{
this->SetParent(parent);
this->Parent(parent);
}
void VerticalListLayout::ApplyLayout() {
if (layout == LayoutOrder::V::TOP)
{
int consumed_height = 0;
for (auto& child_widget : children)
{
// TODO: Implement widget.LayoutOrder property
// TODO: Sort children by LayoutOrder
consumed_height += pad_top.Pixels;
child_widget->Position({0, consumed_height, 0, 0});
consumed_height += child_widget->GetAbsoluteSize().y;
consumed_height += pad_bottom.Pixels;
}
}
if (layout == LayoutOrder::V::BOTTOM)
{
int consumed_height = this->GetAbsoluteSize().y;
for (auto& child_widget : children)
{
// TODO: Implement widget.LayoutOrder property
// TODO: Sort children by LayoutOrder
consumed_height -= pad_top.Pixels;
consumed_height -= child_widget->GetAbsoluteSize().y;
child_widget->Position({0, consumed_height, 0, 0});
consumed_height -= pad_bottom.Pixels;
}
}
}
void VerticalListLayout::LayoutOrder(LayoutOrder::V order) {
layout = order;
}
LayoutOrder::V VerticalListLayout::LayoutOrder() const { return layout;}
HorizontalListLayout::HorizontalListLayout() : ListLayout() {}
HorizontalListLayout::HorizontalListLayout(Widget* parent) : HorizontalListLayout() {
SetParent(parent);
Parent(parent);
}
void HorizontalListLayout::ApplyLayout() {
// TODO: Implement widget.LayoutOrder and sort by that number.
if (layout == LayoutOrder::H::LEFT)
{
int consumed_width = 0;
for (auto &child_widget : children)
{
consumed_width += pad_left.Pixels;
child_widget->Position({consumed_width, 0, 0, 0});
consumed_width += child_widget->GetAbsoluteSize().x;
consumed_width += pad_right.Pixels;
}
}
if (layout == LayoutOrder::H::RIGHT)
{
int consumed_width = this->GetAbsoluteSize().x;
for (auto &child_widget : children)
{
consumed_width -= pad_left.Pixels;
consumed_width -= child_widget->GetAbsoluteSize().x;
child_widget->Position({consumed_width, 0, 0, 0});
consumed_width -= pad_right.Pixels;
}
}
}
void HorizontalListLayout::LayoutOrder(LayoutOrder::H order) {
layout = order;
}
LayoutOrder::H HorizontalListLayout::LayoutOrder() const {
return layout;
}
}

View File

@@ -0,0 +1,121 @@
#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();
//JGL::J2D::Begin();
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);
//JGL::J2D::End();
}
NineSliceRect::NineSliceRect() : Rect(), ImageBase() {}
NineSliceRect::NineSliceRect(Widget *parent) : Rect(parent), ImageBase() {}
NineSliceRect::NineSliceRect(Widget *parent, JGL::Texture *texture) : Rect(parent), ImageBase(texture) {}
}

View File

@@ -1,3 +1,4 @@
#include <JUI/Mixins/Toggleable.hpp>
#include <JUI/Widgets/RadioButton.hpp>
namespace JUI
@@ -5,7 +6,7 @@ namespace JUI
RadioButton::RadioButton() : Button(), Toggleable() {}
RadioButton::RadioButton(Widget* parent) : RadioButton() {
this->SetParent(parent);
this->Parent(parent);
}

View File

@@ -1,56 +1,55 @@
#include "JGL/JGL.h"
#include "JUI/Widgets/Rect.hpp"
#include <JGL/JGL.h>
#include <JUI/Widgets/Rect.hpp>
#include <jlog/Logger.hpp>
namespace JUI {
Rect::Rect(): Widget(), RectBase() {}
Rect::Rect(Widget *parent): Rect() {
this->SetParent(parent);
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()
{
//J2D::Begin();
RectBase::Draw(GetAbsolutePosition(), GetAbsoluteSize());
//J2D::End();
// Draw Child Elements with scissor clipping still active
//Widget::DrawChildWidgets();
}
void Rect::Update(float delta) {
if (IsMouseInside() && !mouse_inside_debounce)
@@ -68,18 +67,24 @@ 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();
AABB2D Rect::GetActualRenderBounds() const {
//DEBUG(std::format("X {} {} Y {} {}", x,pos.x, y,pos.y));
Vector2 border = {border_width, border_width};
if (x > pos.x && y > pos.y && x < pos.x + size.x && y < pos.y + size.y){
return true;
}
return false;
if (border_mode == BorderMode::Inset)
return { GetAbsolutePosition(), GetAbsoluteSize()};
if (border_mode == BorderMode::Outline)
return {GetAbsolutePosition() - border, GetAbsoluteSize() + border};
if (border_mode == BorderMode::Middle)
return { GetAbsolutePosition() - (border/2), GetAbsoluteSize() + (border/2)};
return { GetAbsolutePosition(), GetAbsoluteSize()};
}
void Rect::Draw() {
Widget::Draw();
}
}

View File

@@ -5,15 +5,12 @@ namespace JUI {
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;}
void Scene::Draw() {
Widget::Draw();
}
void Scene::SetViewportSize(int w, int h) {
viewport_w = w;
viewport_h = h;
J2D::Begin();
Widget::Draw();
J2D::End();
}
void Scene::Update(float delta) {

View File

@@ -1,7 +1,87 @@
#include <JUI/Widgets/ScrollingRect.hpp>
namespace JUI
{
using namespace JUI;
}
void ScrollingRect::RecomputeRenderTarget() {
//
//Rect::PreDraw();
InnerDraw();
//Rect::PostDraw();
}
void ScrollingRect::Draw() {
// TODO: Only recompute when something has changed.
// Q: How do we know when something has changed?
RecomputeRenderTarget();
//J2D::DrawRenderTarget(canvas, GetAbsolutePosition());
J2D::DrawPartialRenderTarget(canvas, GetAbsolutePosition(), {0, 0}, Vector2(GetAbsoluteSize().x, GetAbsoluteSize().y));
bool canvas_larger_than_widget = scroll_size > GetAbsoluteSize().y;
float ratio = scroll_size / GetAbsoluteSize().y;
if (vertical_scrollbar_enabled && canvas_larger_than_widget)
{
//std::cout << "Ratio " << ratio << std::endl;
Vector2 vscroll_pos = GetAbsolutePosition() + Vector2(GetAbsoluteSize().x-scrollbar_width, 0);
Vector2 vscroll_size = {scrollbar_width, GetAbsoluteSize().y-scrollbar_width};
J2D::FillRect(Colors::LightGray, vscroll_pos, vscroll_size);
J2D::FillRoundedRect(scrollbar_color, {vscroll_pos.x, vscroll_pos.y + scroll}, {vscroll_size.x, (vscroll_size.y / ratio)}, 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);
}
}
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));
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)) {
bool success = canvas->SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE::MSAA_8X);
}
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(); }

View File

@@ -4,4 +4,115 @@
namespace JUI
{
Slider::Slider(JUI::Widget *parent)
{
this->Parent(parent);
}
void Slider::OnClick(const Vector2 &MousePos, const MouseButton &MouseButton) {
Clickable::OnClick(MousePos, MouseButton);
dragging = true;
}
void Slider::OnRelease(const Vector2 &MousePos, const MouseButton &MouseButton, bool MouseStillOver) {
Clickable::OnRelease(MousePos, MouseButton, MouseStillOver);
dragging = false;
}
void Slider::OnHover(const Vector2 &MousePos) {
Hoverable::OnHover(MousePos);
}
void Slider::OnExit(const Vector2 &MousePos) {
Hoverable::OnExit(MousePos);
}
void Slider::Update(float delta) {
bool selected = Rect::IsMouseInside();
hovered = IsMouseInside();
Hoverable::Update(last_known_mouse_pos, delta);
// TODO: This is duplicated between here and Window.cpp
// Will be abstracted away into Clickable shortly.
if (IsHovered() && mb_state && !prev_mb_state)
{
OnClick(last_known_mouse_pos, mbtn);
}
if (IsClicked() && !mb_state)
{
OnRelease(last_known_mouse_pos, mbtn, IsHovered());
}
prev_mb_state = mb_state;
if (dragging)
{
float range = maximum - minimum;
Vector2 mouse = last_known_mouse_pos;
float slider_abs_left = this->GetAbsolutePosition().x;
float slider_abs_right = (this->GetAbsolutePosition().x + this->GetAbsoluteSize().x) - scrubber_width;
float slider_total_width = this->GetAbsoluteSize().x - scrubber_width;
float scrubberP = Math::Clamp(mouse.x, slider_abs_left, slider_abs_right);
float offset = scrubberP - slider_abs_left;
float percentage = offset / slider_total_width;
float translated = Math::Clamp(roundMultiple(percentage * Range(), interval), 0.f, Range());
float value = translated + minimum;
if (current != value)
ValueChanged(value);
current = value;
//std::cout << "slider value: " << value << std::endl;
}
Rect::Update(delta);
}
void Slider::Draw() {
//scrubber->Draw();
Rect::Draw();
Vector2 pos = {
current * (GetAbsoluteSize().x - scrubber_width) + GetAbsolutePosition().x
,GetAbsolutePosition().y
};
/// TODO: Implement internal padding on scrubber element?
//J2D::Begin();
JGL::J2D::FillRect(scrubber_color, pos, {scrubber_width, GetAbsoluteSize().y});
//J2D::End();
}
float Slider::Minimum() const { return minimum; }
float Slider::Maximum() const { return maximum; }
float Slider::Interval() const { return interval; }
float Slider::CurrentValue() const { return current; }
Color4 Slider::ScrubberColor() const { return scrubber_color; }
float Slider::ScrubberWidth() const { return scrubber_width; }
float Slider::Range() const { return maximum - minimum; }
void Slider::Minimum(float min) { minimum = min; }
void Slider::Maximum(float max) { maximum = max; }
void Slider::Interval(float inter) { interval = inter; }
void Slider::CurrentValue(float value) { current = value; }
void Slider::ScrubberColor(const Color4 &color) { scrubber_color = color;}
void Slider::ScrubberWidth(float width) { scrubber_width = width;}
}

View File

@@ -6,12 +6,12 @@ namespace JUI {
Text::Text(Widget* parent) : Text()
{
this->SetParent(parent);
this->Parent(parent);
}
Text::~Text() {}
Vector2 Text::GetAbsolutePosition() const {
auto parent =this->GetParent();
auto parent = this->GetParent();
if (parent)
return parent->GetAbsolutePosition();
//return {0,0};

View File

@@ -1,3 +1,20 @@
//
// Created by dawsh on 7/16/24.
//
#include <JUI/Widgets/TextButton.hpp>
namespace JUI
{
TextButton::TextButton() : Button(), TextBase() {}
TextButton::TextButton(Widget* parent) : TextButton() {
this->Parent(parent);
}
void TextButton::Update(float delta) {
Button::Update(delta);
TextBase::Update(delta);
}
void TextButton::Draw() {
Button::Draw();
// TODO: Factor in padding here.
TextBase::Draw(this->GetAbsolutePosition()+GetAbsolutePaddingTopLeft(), this->GetAbsoluteSize()-GetAbsolutePaddingBottomRight());
}
}

View File

@@ -0,0 +1,216 @@
#include <JUI/Widgets/TextInputForm.hpp>
#include <ReWindow/InputService.h>
namespace JUI {
TextInputForm::TextInputForm() : TextRect(), Clickable() { }
TextInputForm::TextInputForm(Widget* parent) : TextInputForm() {
this->Parent(parent);
}
void TextInputForm::ObserveMouseMovement(const Vector2 &latest_known_pos) {
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; }
void TextInputForm::ObserveMouseInput(MouseButton btn, bool pressed) {
Widget::ObserveMouseInput(btn, pressed);
if (pressed && btn == MouseButton::Left) {
if (IsMouseInside()) {
if (!focused) {
OnSelect.Invoke();
}
focused = true;
}
else {
if (focused)
OnDeselect.Invoke();
focused = false;
}
}
}
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);
//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::ObserveKeyInput(Key key, bool pressed) {
Widget::ObserveKeyInput(key, pressed);
if (!pressed)
return;
if (!focused)
return;
// TODO: Text Selection
// TODO: Support Copy
// TODO: Support Paste
// TODO: Support Cut
// TODO: Support insert at cursor
// TODO: Simulate key repeat.
if (key == Keys::Return || key == Keys::NumPadReturn) {
OnReturn.Invoke(input_buffer);
if (clear_text_on_return) {
input_buffer = "";
cursor_position = 0;
}
return;
}
if (key == Keys::LeftArrow) {
if (cursor_position > 0)
cursor_position -= 1;
return;
}
if (key == Keys::RightArrow) {
if (cursor_position < input_buffer.length() - 1)
cursor_position += 1;
return;
}
if (InputService::IsKeyDown(Keys::LeftControl)) {
if (key == Keys::C) {
// TODO: Implement Copy
return;
}
if (key == Keys::V) {
// TODO: Implement Paste
return;
}
if (key == Keys::X) {
// TODO: Implement Cut
cursor_position = 0; // TODO: Set to size of pasted content.
return;
}
//input_buffer = input_buffer.insert(cursor_position, uppercase( key.Mnemonic));
//return;
}
if (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) {
return;
}
if (key == Keys::Backspace) {
if (cursor_position > 0) {
input_buffer = input_buffer.erase(cursor_position-1, 1);
//input_buffer = input_buffer.substr(0, input_buffer.length()-1);
cursor_position--;
}
} else {
if (InputService::IsKeyDown(Keys::LeftShift) || InputService::IsKeyDown(Keys::RightShift)) {
input_buffer = input_buffer.insert(cursor_position, uppercase( key.Mnemonic));
cursor_position++;
} else {
input_buffer = input_buffer.insert(cursor_position, lowercase( key.Mnemonic));
cursor_position++;
}
//SetContent(GetContent() + key.Mnemonic);
}
}
bool TextInputForm::HasFocus() const { return focused; }
void TextInputForm::SetFocused(bool focused) { this->focused = focused;}
void TextInputForm::GrabFocus() { SetFocused(true); }
void TextInputForm::DropFocus() { SetFocused(false); }
}

View File

@@ -3,12 +3,12 @@
namespace JUI {
TextRect::TextRect() : Rect(), TextBase() {}
TextRect::TextRect(Widget* parent) : TextRect() {
this->SetParent(parent);
this->Parent(parent);
}
void TextRect::Update(float delta) {
if (fit_text)
if (fit_size_to_text)
{
Vector2 abs_pos = this->GetAbsolutePosition();
@@ -39,11 +39,9 @@ namespace JUI {
}
void TextRect::Draw() {
Rect::Draw();
void TextRect::InnerDraw()
{
Rect::InnerDraw();
Vector2 abs_pos = this->GetAbsolutePosition();
Vector2 abs_size = this->GetAbsoluteSize();
@@ -60,6 +58,11 @@ namespace JUI {
TextBase::Draw(abs_pos + pad_shifts_pos_by, abs_size - pad_shrinks_size_by*2);
}
void TextRect::Draw() {
Rect::Draw();
}
}

View File

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

View File

@@ -6,33 +6,13 @@
namespace JUI
{
Window::Window() : Widget(), Clickable(), Hoverable(), RectBase(), Draggable(), Resizable(), Dockable() {
this->SetPosition({200, 200, 0, 0});
this->SetSize({400, 200, 0, 0});
this->Position({200, 200, 0, 0});
this->Size({400, 200, 0, 0});
min_size = {400, 200};
this->BGColor({0,0,0,0});
this->BGColor({64,64,64,255});
this->BorderColor({92,92,192});
this->SetBorderWidth(2);
this->SetBorderMode(BorderMode::Outline);
//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")
};
*/
this->BorderMode(BorderMode::Middle);
// TODO: Move out of Event callback
this->OnReleaseEvent += [&] (Vector2 dummy, MouseButton btn, bool dummy3) {
@@ -42,64 +22,90 @@ namespace JUI
};
Viewport = new Rect(this);
Viewport->SetName("Viewport");
Viewport->BGColor({64,64,64, 255});
Viewport->SetSize({0, -20, 1, 1});
Viewport->SetPosition({0,20,0,0});
// TODO: Viewport->SetAnchorPoint({0.f, 0.f});
//Viewport->BorderColor({128, 128, 128, 255});
//Viewport->SetBorderWidth(1);
//Viewport->SetPadding(1_px);
Viewport->Name("Viewport");
Viewport->BGColor({64,64,64, 0});
Viewport->Size({0, -20, 1, 1});
Viewport->Position({0, 20, 0, 0});
// TODO: Viewport->AnchorPoint({0.f, 0.f});
Viewport->BorderColor({128, 128, 128, 255});
Viewport->SetBorderWidth(0);
//Viewport->Padding(1_px);
// TODO: Viewport->SetBorderRadius(0);
Topbar = new Rect(this);
Topbar->SetPosition({0_px, 0_px});
Topbar->SetSize({100_percent, 20_px});
Topbar->Position({0_px, 0_px});
Topbar->Size({100_percent, UDim(titlebar_height, 0)});
Topbar->BGColor({92,92,192, 255});
//Topbar->BorderColor({128, 128, 128, 255});
//Topbar->SetBorderWidth(1);
Topbar->BorderColor({128, 128, 128, 0});
Topbar->SetBorderWidth(0);
TitleLabel = new Text(Topbar);
TitleLabel->Center();
TitleLabel->SetContent(title);
TitleLabel->SetTextSize(titlebar_height);
TitleLabel->SetTextSize(title_font_size);
// TODO: auto* list = new HorizontalListLayout(Topbar);
// TODO: exit_btn
exit_btn = new TextButton(Topbar);
//exit_btn->SetAnchorPoint({1.f, 0.f});
//exit_btn->SetSize({30_px, 100_percent});
exit_btn->SetSize({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
auto* exb_tex = new Texture({titlebar_height, titlebar_height });
auto* exb_rt = new RenderTarget(exb_tex);
if (exb_rt->SetMSAAEnabled(JGL::MSAA_SAMPLE_RATE::MSAA_8X)) {/* using msaa to make the circle nicer. */}
std::array<Color4, 2> exb_circle_colors
{
Color4(168, 28, 28, 255),
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->BaseBGColor({92,92,192, 255});
exit_btn->BGColor(exit_btn->BaseBGColor());
exit_btn->BorderColors({0,0,0,0}, {0,0,0,0}, {0,0,0,0}, {0,0,0,0});
exit_btn->BorderColor({0,0,0,0});
exit_btn->SetBorderWidth(0.f);
//exit_btn_text->SetFont(TitleLabel->GetFont());
exit_btn->SetTextSize(titlebar_height);
exit_btn->SetTextColor({255, 0, 0});
//exit_btn->SetTextSize(title_font_size);
//exit_btn->SetTextColor({255, 0, 0});
//exit_btn->Center();
exit_btn->OnClickEvent += [&] (auto... _) {
this->SetVisible(false);
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() {
this->SetParent(parent);
this->Parent(parent);
}
Vector2 Window::MinSize() const { return min_size; }
@@ -116,11 +122,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);
@@ -129,7 +135,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)
@@ -137,10 +143,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();
@@ -160,9 +166,9 @@ namespace JUI
void Window::Update(float delta) {
if (dragging) {
jlog::Debug(std::format("mpos {} {}", last_known_mouse_pos.x, last_known_mouse_pos.y));
//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->SetPosition(UDim2{(int)mpos.x, (int)mpos.y, 0,0});
this->Position(UDim2{(int) mpos.x, (int) mpos.y, 0, 0});
}
if (resizing) {
@@ -170,7 +176,7 @@ namespace JUI
Vector2 resize_amt = mpos - initial_resize_offset;
SetSize(size_when_restart_began + UDim2(resize_amt.x, resize_amt.y, 0, 0));
Size(size_when_restart_began + UDim2(resize_amt.x, resize_amt.y, 0, 0));
Vector2 abs_size = this->GetAbsoluteSize();
@@ -178,14 +184,14 @@ namespace JUI
if (abs_size.x < min_size.x)
{
float needed_amt_to_resize = min_size.x - abs_size.x;
SetSize(Size() + UDim2(needed_amt_to_resize, 0, 0, 0));
Size(Size() + UDim2(needed_amt_to_resize, 0, 0, 0));
}
// Clamp Y-axis.
if (abs_size.y < min_size.y)
{
float needed_amt_to_resize = min_size.y - abs_size.y;
SetSize(Size() + UDim2(0, needed_amt_to_resize, 0, 0));
Size(Size() + UDim2(0, needed_amt_to_resize, 0, 0));
}
}
@@ -218,6 +224,52 @@ namespace JUI
}
void Window::SetTitle(const std::string &title) {
GetTitleInstance()->SetContent(title);
TitleInstance()->SetContent(title);
}
void Window::MinSize(const Vector2 &constraint) {
min_size = constraint;
}
void Window::MaxSize(const Vector2 &constraint) {
max_size = constraint;
}
void Window::SetDraggable(bool value) {
draggable = value;
}
void Window::SetDockable(bool value) {
dockable = value;
}
void Window::SetResizable(bool value) {
resizable = value;
}
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()};
}
}