Add zwidget

This commit is contained in:
Magnus Norddahl 2023-12-27 00:44:40 +01:00
commit b2d2f61be0
54 changed files with 9321 additions and 0 deletions

View file

@ -0,0 +1,63 @@
#pragma once
#include <memory>
#include <string>
class Font;
class Image;
class Point;
class Rect;
class Colorf;
class DisplayWindow;
struct VerticalTextPosition;
class FontMetrics
{
public:
double ascent = 0.0;
double descent = 0.0;
double external_leading = 0.0;
double height = 0.0;
};
class Canvas
{
public:
static std::unique_ptr<Canvas> create(DisplayWindow* window);
virtual ~Canvas() = default;
virtual void begin(const Colorf& color) = 0;
virtual void end() = 0;
virtual void begin3d() = 0;
virtual void end3d() = 0;
virtual Point getOrigin() = 0;
virtual void setOrigin(const Point& origin) = 0;
virtual void pushClip(const Rect& box) = 0;
virtual void popClip() = 0;
virtual void fillRect(const Rect& box, const Colorf& color) = 0;
virtual void line(const Point& p0, const Point& p1, const Colorf& color) = 0;
virtual void drawText(const Point& pos, const Colorf& color, const std::string& text) = 0;
virtual Rect measureText(const std::string& text) = 0;
virtual VerticalTextPosition verticalTextAlign() = 0;
virtual void drawText(const std::shared_ptr<Font>& font, const Point& pos, const std::string& text, const Colorf& color) = 0;
virtual void drawTextEllipsis(const std::shared_ptr<Font>& font, const Point& pos, const Rect& clipBox, const std::string& text, const Colorf& color) = 0;
virtual Rect measureText(const std::shared_ptr<Font>& font, const std::string& text) = 0;
virtual FontMetrics getFontMetrics(const std::shared_ptr<Font>& font) = 0;
virtual int getCharacterIndex(const std::shared_ptr<Font>& font, const std::string& text, const Point& hitPoint) = 0;
virtual void drawImage(const std::shared_ptr<Image>& image, const Point& pos) = 0;
};
struct VerticalTextPosition
{
double top = 0.0;
double baseline = 0.0;
double bottom = 0.0;
};

View file

@ -0,0 +1,36 @@
#pragma once
#include <cstdint>
#include <cmath>
class Colorf
{
public:
Colorf() = default;
Colorf(float r, float g, float b, float a = 1.0f) : r(r), g(g), b(b), a(a) { }
static Colorf transparent() { return { 0.0f, 0.0f, 0.0f, 0.0f }; }
static Colorf fromRgba8(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255)
{
float s = 1.0f / 255.0f;
return { r * s, g * s, b * s, a * s };
}
uint32_t toBgra8() const
{
uint32_t cr = (int)(std::max(std::min(r * 255.0f, 255.0f), 0.0f));
uint32_t cg = (int)(std::max(std::min(g * 255.0f, 255.0f), 0.0f));
uint32_t cb = (int)(std::max(std::min(b * 255.0f, 255.0f), 0.0f));
uint32_t ca = (int)(std::max(std::min(a * 255.0f, 255.0f), 0.0f));
return (ca << 24) | (cr << 16) | (cg << 8) | cb;
}
bool operator==(const Colorf& v) const { return r == v.r && g == v.g && b == v.b && a == v.a; }
bool operator!=(const Colorf& v) const { return r != v.r || g != v.g || b != v.b || a != v.a; }
float r = 0.0f;
float g = 0.0f;
float b = 0.0f;
float a = 1.0f;
};

View file

@ -0,0 +1,15 @@
#pragma once
#include <memory>
#include <string>
class Font
{
public:
virtual ~Font() = default;
virtual const std::string& GetName() const = 0;
virtual double GetHeight() const = 0;
static std::shared_ptr<Font> Create(const std::string& name, double height);
};

View file

@ -0,0 +1,22 @@
#pragma once
#include <memory>
enum class ImageFormat
{
R8G8B8A8,
B8G8R8A8
};
class Image
{
public:
virtual ~Image() = default;
virtual int GetWidth() const = 0;
virtual int GetHeight() const = 0;
virtual ImageFormat GetFormat() const = 0;
virtual void* GetData() const = 0;
static std::shared_ptr<Image> Create(int width, int height, ImageFormat format, const void* data);
};

View file

@ -0,0 +1,74 @@
#pragma once
class Point
{
public:
Point() = default;
Point(double x, double y) : x(x), y(y) { }
double x = 0;
double y = 0;
Point& operator+=(const Point& p) { x += p.x; y += p.y; return *this; }
Point& operator-=(const Point& p) { x -= p.x; y -= p.y; return *this; }
};
class Size
{
public:
Size() = default;
Size(double width, double height) : width(width), height(height) { }
double width = 0;
double height = 0;
};
class Rect
{
public:
Rect() = default;
Rect(const Point& p, const Size& s) : x(p.x), y(p.y), width(s.width), height(s.height) { }
Rect(double x, double y, double width, double height) : x(x), y(y), width(width), height(height) { }
Point pos() const { return { x, y }; }
Size size() const { return { width, height }; }
Point topLeft() const { return { x, y }; }
Point topRight() const { return { x + width, y }; }
Point bottomLeft() const { return { x, y + height }; }
Point bottomRight() const { return { x + width, y + height }; }
double left() const { return x; }
double top() const { return y; }
double right() const { return x + width; }
double bottom() const { return y + height; }
static Rect xywh(double x, double y, double width, double height) { return Rect(x, y, width, height); }
static Rect ltrb(double left, double top, double right, double bottom) { return Rect(left, top, right - left, bottom - top); }
bool contains(const Point& p) const { return (p.x >= x && p.x < x + width) && (p.y >= y && p.y < y + height); }
double x = 0;
double y = 0;
double width = 0;
double height = 0;
};
inline Point operator+(const Point& a, const Point& b) { return Point(a.x + b.x, a.y + b.y); }
inline Point operator-(const Point& a, const Point& b) { return Point(a.x - b.x, a.y - b.y); }
inline bool operator==(const Point& a, const Point& b) { return a.x == b.x && a.y == b.y; }
inline bool operator!=(const Point& a, const Point& b) { return a.x != b.x || a.y != b.y; }
inline bool operator==(const Size& a, const Size& b) { return a.width == b.width && a.height == b.height; }
inline bool operator!=(const Size& a, const Size& b) { return a.width != b.width || a.height != b.height; }
inline bool operator==(const Rect& a, const Rect& b) { return a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height; }
inline bool operator!=(const Rect& a, const Rect& b) { return a.x != b.x || a.y != b.y || a.width != b.width || a.height != b.height; }
inline Point operator+(const Point& a, double b) { return Point(a.x + b, a.y + b); }
inline Point operator-(const Point& a, double b) { return Point(a.x - b, a.y - b); }
inline Point operator*(const Point& a, double b) { return Point(a.x * b, a.y * b); }
inline Point operator/(const Point& a, double b) { return Point(a.x / b, a.y / b); }
inline Size operator+(const Size& a, double b) { return Size(a.width + b, a.height + b); }
inline Size operator-(const Size& a, double b) { return Size(a.width - b, a.height - b); }
inline Size operator*(const Size& a, double b) { return Size(a.width * b, a.height * b); }
inline Size operator/(const Size& a, double b) { return Size(a.width / b, a.height / b); }

View file

@ -0,0 +1,7 @@
#pragma once
#include <vector>
#include <cstdint>
#include <string>
std::vector<uint8_t> LoadWidgetFontData(const std::string& name);

View file

@ -0,0 +1,238 @@
#pragma once
#include <vector>
#include <algorithm>
#include "colorf.h"
#include "rect.h"
#include "font.h"
#include "canvas.h"
class Widget;
class Image;
class Canvas;
enum SpanAlign
{
span_left,
span_right,
span_center,
span_justify
};
class SpanLayout
{
public:
SpanLayout();
~SpanLayout();
struct HitTestResult
{
enum Type
{
no_objects_available,
outside_top,
outside_left,
outside_right,
outside_bottom,
inside
};
Type type = {};
int object_id = -1;
size_t offset = 0;
};
void Clear();
void AddText(const std::string& text, std::shared_ptr<Font> font, const Colorf& color = Colorf(), int id = -1);
void AddImage(const std::shared_ptr<Image> image, double baseline_offset = 0, int id = -1);
void AddWidget(Widget* component, double baseline_offset = 0, int id = -1);
void Layout(Canvas* canvas, double max_width);
void SetPosition(const Point& pos);
Size GetSize() const;
Rect GetRect() const;
std::vector<Rect> GetRectById(int id) const;
HitTestResult HitTest(Canvas* canvas, const Point& pos);
void DrawLayout(Canvas* canvas);
/// Draw layout generating ellipsis for clipped text
void DrawLayoutEllipsis(Canvas* canvas, const Rect& content_rect);
void SetComponentGeometry();
Size FindPreferredSize(Canvas* canvas);
void SetSelectionRange(std::string::size_type start, std::string::size_type end);
void SetSelectionColors(const Colorf& foreground, const Colorf& background);
void ShowCursor();
void HideCursor();
void SetCursorPos(std::string::size_type pos);
void SetCursorOverwriteMode(bool enable);
void SetCursorColor(const Colorf& color);
std::string GetCombinedText() const;
void SetAlign(SpanAlign align);
double GetFirstBaselineOffset();
double GetLastBaselineOffset();
private:
struct TextBlock
{
size_t start = 0;
size_t end = 0;
};
enum ObjectType
{
object_text,
object_image,
object_component
};
enum FloatType
{
float_none,
float_left,
float_right
};
struct SpanObject
{
ObjectType type = object_text;
FloatType float_type = float_none;
std::shared_ptr<Font> font;
Colorf color;
size_t start = 0, end = 0;
std::shared_ptr<Image> image;
Widget* component = nullptr;
double baseline_offset = 0;
int id = -1;
};
struct LineSegment
{
ObjectType type = object_text;
std::shared_ptr<Font> font;
Colorf color;
size_t start = 0;
size_t end = 0;
double ascender = 0;
double descender = 0;
double x_position = 0;
double width = 0;
std::shared_ptr<Image> image;
Widget* component = nullptr;
double baseline_offset = 0;
int id = -1;
};
struct Line
{
double width = 0; // Width of the entire line (including spaces)
double height = 0;
double ascender = 0;
std::vector<LineSegment> segments;
};
struct TextSizeResult
{
size_t start = 0;
size_t end = 0;
double width = 0;
double height = 0;
double ascender = 0;
double descender = 0;
int objects_traversed = 0;
std::vector<LineSegment> segments;
};
struct CurrentLine
{
std::vector<SpanObject>::size_type object_index = 0;
Line cur_line;
double x_position = 0;
double y_position = 0;
};
struct FloatBox
{
Rect rect;
ObjectType type = object_image;
std::shared_ptr<Image> image;
Widget* component = nullptr;
int id = -1;
};
TextSizeResult FindTextSize(Canvas* canvas, const TextBlock& block, size_t object_index);
std::vector<TextBlock> FindTextBlocks();
void LayoutLines(Canvas* canvas, double max_width);
void LayoutText(Canvas* canvas, std::vector<TextBlock> blocks, std::vector<TextBlock>::size_type block_index, CurrentLine& current_line, double max_width);
void LayoutBlock(CurrentLine& current_line, double max_width, std::vector<TextBlock>& blocks, std::vector<TextBlock>::size_type block_index);
void LayoutFloatBlock(CurrentLine& current_line, double max_width);
void LayoutInlineBlock(CurrentLine& current_line, double max_width, std::vector<TextBlock>& blocks, std::vector<TextBlock>::size_type block_index);
void ReflowLine(CurrentLine& current_line, double max_width);
FloatBox FloatBoxLeft(FloatBox float_box, double max_width);
FloatBox FloatBoxRight(FloatBox float_box, double max_width);
FloatBox FloatBoxAny(FloatBox box, double max_width, const std::vector<FloatBox>& floats1);
bool BoxFitsOnLine(const FloatBox& box, double max_width);
void PlaceLineSegments(CurrentLine& current_line, TextSizeResult& text_size_result);
void ForcePlaceLineSegments(CurrentLine& current_line, TextSizeResult& text_size_result, double max_width);
void NextLine(CurrentLine& current_line);
bool IsNewline(const TextBlock& block);
bool IsWhitespace(const TextBlock& block);
bool FitsOnLine(double x_position, const TextSizeResult& text_size_result, double max_width);
bool LargerThanLine(const TextSizeResult& text_size_result, double max_width);
void AlignJustify(double max_width);
void AlignCenter(double max_width);
void AlignRight(double max_width);
void DrawLayoutImage(Canvas* canvas, Line& line, LineSegment& segment, double x, double y);
void DrawLayoutText(Canvas* canvas, Line& line, LineSegment& segment, double x, double y);
bool cursor_visible = false;
std::string::size_type cursor_pos = 0;
bool cursor_overwrite_mode = false;
Colorf cursor_color;
std::string::size_type sel_start = 0, sel_end = 0;
Colorf sel_foreground, sel_background = Colorf::fromRgba8(153, 201, 239);
std::string text;
std::vector<SpanObject> objects;
std::vector<Line> lines;
Point position;
std::vector<FloatBox> floats_left, floats_right;
SpanAlign alignment = span_left;
struct LayoutCache
{
int object_index = -1;
FontMetrics metrics;
};
LayoutCache layout_cache;
bool is_ellipsis_draw = false;
Rect ellipsis_content_rect;
template<typename T>
static T clamp(T val, T minval, T maxval) { return std::max<T>(std::min<T>(val, maxval), minval); }
};

View file

@ -0,0 +1,24 @@
#pragma once
#include <functional>
class Widget;
class Timer
{
public:
Timer(Widget* owner);
~Timer();
void Start(int timeoutMilliseconds, bool repeat = true);
void Stop();
std::function<void()> FuncExpired;
private:
Widget* OwnerObj = nullptr;
Timer* PrevTimerObj = nullptr;
Timer* NextTimerObj = nullptr;
friend class Widget;
};

View file

@ -0,0 +1,78 @@
/*
** Copyright (c) 1997-2015 Mark Page
**
** This software is provided 'as-is', without any express or implied
** warranty. In no event will the authors be held liable for any damages
** arising from the use of this software.
**
** Permission is granted to anyone to use this software for any purpose,
** including commercial applications, and to alter it and redistribute it
** freely, subject to the following restrictions:
**
** 1. The origin of this software must not be misrepresented; you must not
** claim that you wrote the original software. If you use this software
** in a product, an acknowledgment in the product documentation would be
** appreciated but is not required.
** 2. Altered source versions must be plainly marked as such, and must not be
** misrepresented as being the original software.
** 3. This notice may not be removed or altered from any source distribution.
**
*/
#pragma once
#include <string>
/// \brief UTF8 reader helper functions.
class UTF8Reader
{
public:
/// Important: text is not copied by this class and must remain valid during its usage.
UTF8Reader(const std::string::value_type *text, std::string::size_type length);
/// \brief Returns true if the current position is at the end of the string
bool is_end();
/// \brief Get the character at the current position
unsigned int character();
/// \brief Returns the length of the current character
std::string::size_type char_length();
/// \brief Moves position to the previous character
void prev();
/// \brief Moves position to the next character
void next();
/// \brief Moves position to the lead byte of the character
void move_to_leadbyte();
/// \brief Get the current position of the reader
std::string::size_type position();
/// \brief Set the current position of the reader
void set_position(std::string::size_type position);
static size_t utf8_length(const std::string& text)
{
return utf8_length(text.data(), text.size());
}
static size_t utf8_length(const std::string::value_type* text, std::string::size_type length)
{
UTF8Reader reader(text, length);
size_t i = 0;
while (!reader.is_end())
{
reader.next();
i++;
}
return i;
}
private:
std::string::size_type current_position = 0;
std::string::size_type length = 0;
const unsigned char *data = nullptr;
};

View file

@ -0,0 +1,181 @@
#pragma once
#include <string>
#include <memory>
#include "canvas.h"
#include "rect.h"
#include "colorf.h"
#include "../window/window.h"
class Canvas;
class Timer;
enum class WidgetType
{
Child,
Window,
Popup
};
class Widget : DisplayWindowHost
{
public:
Widget(Widget* parent = nullptr, WidgetType type = WidgetType::Child);
virtual ~Widget();
void SetParent(Widget* parent);
void MoveBefore(Widget* sibling);
std::string GetWindowTitle() const;
void SetWindowTitle(const std::string& text);
// Icon GetWindowIcon() const;
// void SetWindowIcon(const Icon& icon);
// Widget content box
Size GetSize() const;
double GetWidth() const { return GetSize().width; }
double GetHeight() const { return GetSize().height; }
// Widget noncontent area
void SetNoncontentSizes(double left, double top, double right, double bottom);
// Widget frame box
Rect GetFrameGeometry() const;
void SetFrameGeometry(const Rect& geometry);
void SetFrameGeometry(double x, double y, double width, double height) { SetFrameGeometry(Rect::xywh(x, y, width, height)); }
void SetWindowBackground(const Colorf& color);
void SetWindowBorderColor(const Colorf& color);
void SetWindowCaptionColor(const Colorf& color);
void SetWindowCaptionTextColor(const Colorf& color);
void SetVisible(bool enable) { if (enable) Show(); else Hide(); }
void Show();
void ShowFullscreen();
void ShowMaximized();
void ShowMinimized();
void ShowNormal();
void Hide();
void ActivateWindow();
void Close();
void Update();
void Repaint();
bool HasFocus();
bool IsEnabled();
bool IsVisible();
void SetFocus();
void SetEnabled(bool value);
void SetDisabled(bool value) { SetEnabled(!value); }
void SetHidden(bool value) { if (value) Hide(); else Show(); }
void LockCursor();
void UnlockCursor();
void SetCursor(StandardCursor cursor);
void CaptureMouse();
void ReleaseMouseCapture();
bool GetKeyState(EInputKey key);
std::string GetClipboardText();
void SetClipboardText(const std::string& text);
Widget* Window();
Canvas* GetCanvas();
Widget* ChildAt(double x, double y) { return ChildAt(Point(x, y)); }
Widget* ChildAt(const Point& pos);
Widget* Parent() const { return ParentObj; }
Widget* PrevSibling() const { return PrevSiblingObj; }
Widget* NextSibling() const { return NextSiblingObj; }
Widget* FirstChild() const { return FirstChildObj; }
Widget* LastChild() const { return LastChildObj; }
Point MapFrom(const Widget* parent, const Point& pos) const;
Point MapFromGlobal(const Point& pos) const;
Point MapFromParent(const Point& pos) const { return MapFrom(Parent(), pos); }
Point MapTo(const Widget* parent, const Point& pos) const;
Point MapToGlobal(const Point& pos) const;
Point MapToParent(const Point& pos) const { return MapTo(Parent(), pos); }
protected:
virtual void OnPaintFrame(Canvas* canvas) { }
virtual void OnPaint(Canvas* canvas) { }
virtual void OnMouseMove(const Point& pos) { }
virtual void OnMouseDown(const Point& pos, int key) { }
virtual void OnMouseDoubleclick(const Point& pos, int key) { }
virtual void OnMouseUp(const Point& pos, int key) { }
virtual void OnMouseWheel(const Point& pos, EInputKey key) { }
virtual void OnMouseLeave() { }
virtual void OnRawMouseMove(int dx, int dy) { }
virtual void OnKeyChar(std::string chars) { }
virtual void OnKeyDown(EInputKey key) { }
virtual void OnKeyUp(EInputKey key) { }
virtual void OnGeometryChanged() { }
virtual void OnClose() { delete this; }
virtual void OnSetFocus() { }
virtual void OnLostFocus() { }
virtual void OnEnableChanged() { }
private:
void DetachFromParent();
void Paint(Canvas* canvas);
// DisplayWindowHost
void OnWindowPaint() override;
void OnWindowMouseMove(const Point& pos) override;
void OnWindowMouseDown(const Point& pos, EInputKey key) override;
void OnWindowMouseDoubleclick(const Point& pos, EInputKey key) override;
void OnWindowMouseUp(const Point& pos, EInputKey key) override;
void OnWindowMouseWheel(const Point& pos, EInputKey key) override;
void OnWindowRawMouseMove(int dx, int dy) override;
void OnWindowKeyChar(std::string chars) override;
void OnWindowKeyDown(EInputKey key) override;
void OnWindowKeyUp(EInputKey key) override;
void OnWindowGeometryChanged() override;
void OnWindowClose() override;
void OnWindowActivated() override;
void OnWindowDeactivated() override;
void OnWindowDpiScaleChanged() override;
WidgetType Type = {};
Widget* ParentObj = nullptr;
Widget* PrevSiblingObj = nullptr;
Widget* NextSiblingObj = nullptr;
Widget* FirstChildObj = nullptr;
Widget* LastChildObj = nullptr;
Timer* FirstTimerObj = nullptr;
Rect FrameGeometry = Rect::xywh(0.0, 0.0, 0.0, 0.0);
Rect ContentGeometry = Rect::xywh(0.0, 0.0, 0.0, 0.0);
Colorf WindowBackground = Colorf::fromRgba8(240, 240, 240);
struct
{
double Left = 0.0;
double Top = 0.0;
double Right = 0.0;
double Bottom = 0.0;
} Noncontent;
std::string WindowTitle;
std::unique_ptr<DisplayWindow> DispWindow;
std::unique_ptr<Canvas> DispCanvas;
Widget* FocusWidget = nullptr;
Widget* CaptureWidget = nullptr;
Widget(const Widget&) = delete;
Widget& operator=(const Widget&) = delete;
friend class Timer;
};

View file

@ -0,0 +1,25 @@
#pragma once
#include "../../core/widget.h"
class CheckboxLabel : public Widget
{
public:
CheckboxLabel(Widget* parent = nullptr);
void SetText(const std::string& value);
const std::string& GetText() const;
void SetChecked(bool value);
bool GetChecked() const;
double GetPreferredHeight() const;
protected:
void OnPaint(Canvas* canvas) override;
private:
std::string text;
bool checked = false;
};

View file

@ -0,0 +1,21 @@
#pragma once
#include "../../core/widget.h"
#include "../../core/image.h"
class ImageBox : public Widget
{
public:
ImageBox(Widget* parent);
void SetImage(std::shared_ptr<Image> newImage);
double GetPreferredHeight() const;
protected:
void OnPaint(Canvas* canvas) override;
private:
std::shared_ptr<Image> image;
};

View file

@ -0,0 +1,160 @@
#pragma once
#include "../../core/widget.h"
#include "../../core/timer.h"
#include <functional>
class LineEdit : public Widget
{
public:
LineEdit(Widget* parent);
~LineEdit();
enum Alignment
{
align_left,
align_center,
align_right
};
Alignment GetAlignment() const;
bool IsReadOnly() const;
bool IsLowercase() const;
bool IsUppercase() const;
bool IsPasswordMode() const;
int GetMaxLength() const;
std::string GetText() const;
int GetTextInt() const;
float GetTextFloat() const;
std::string GetSelection() const;
int GetSelectionStart() const;
int GetSelectionLength() const;
int GetCursorPos() const;
Size GetTextSize();
Size GetTextSize(const std::string& str);
double GetPreferredContentWidth();
double GetPreferredContentHeight(double width);
void SetSelectAllOnFocusGain(bool enable);
void SelectAll();
void SetAlignment(Alignment alignment);
void SetReadOnly(bool enable = true);
void SetLowercase(bool enable = true);
void SetUppercase(bool enable = true);
void SetPasswordMode(bool enable = true);
void SetNumericMode(bool enable = true, bool decimals = false);
void SetMaxLength(int length);
void SetText(const std::string& text);
void SetTextInt(int number);
void SetTextFloat(float number, int num_decimal_places = 6);
void SetSelection(int pos, int length);
void ClearSelection();
void SetCursorPos(int pos);
void DeleteSelectedText();
void SetInputMask(const std::string& mask);
void SetDecimalCharacter(const std::string& decimal_char);
std::function<bool(int key)> FuncIgnoreKeyDown;
std::function<std::string(std::string text)> FuncFilterKeyChar;
std::function<void()> FuncBeforeEditChanged;
std::function<void()> FuncAfterEditChanged;
std::function<void()> FuncSelectionChanged;
std::function<void()> FuncFocusGained;
std::function<void()> FuncFocusLost;
std::function<void()> FuncEnterPressed;
protected:
void OnPaintFrame(Canvas* canvas) override;
void OnPaint(Canvas* canvas) override;
void OnMouseMove(const Point& pos) override;
void OnMouseDown(const Point& pos, int key) override;
void OnMouseDoubleclick(const Point& pos, int key) override;
void OnMouseUp(const Point& pos, int key) override;
void OnKeyChar(std::string chars) override;
void OnKeyDown(EInputKey key) override;
void OnKeyUp(EInputKey key) override;
void OnGeometryChanged() override;
void OnEnableChanged() override;
void OnSetFocus() override;
void OnLostFocus() override;
private:
void OnTimerExpired();
void OnScrollTimerExpired();
void UpdateTextClipping();
void Move(int steps, bool ctrl, bool shift);
bool InsertText(int pos, const std::string& str);
void Backspace();
void Del();
int GetCharacterIndex(double x);
int FindNextBreakCharacter(int pos);
int FindPreviousBreakCharacter(int pos);
std::string GetVisibleTextBeforeSelection();
std::string GetVisibleTextAfterSelection();
std::string GetVisibleSelectedText();
std::string CreatePassword(std::string::size_type num_letters) const;
Size GetVisualTextSize(Canvas* canvas, int pos, int npos) const;
Size GetVisualTextSize(Canvas* canvas) const;
Rect GetCursorRect();
Rect GetSelectionRect();
bool InputMaskAcceptsInput(int cursor_pos, const std::string& str);
void SetSelectionStart(int start);
void SetSelectionLength(int length);
void SetTextSelection(int start, int length);
static std::string ToFixed(float number, int num_decimal_places);
static std::string ToLower(const std::string& text);
static std::string ToUpper(const std::string& text);
Timer* timer = nullptr;
std::string text;
Alignment alignment = align_left;
int cursor_pos = 0;
int max_length = -1;
bool mouse_selecting = false;
bool lowercase = false;
bool uppercase = false;
bool password_mode = false;
bool numeric_mode = false;
bool numeric_mode_decimals = false;
bool readonly = false;
int selection_start = -1;
int selection_length = 0;
std::string input_mask;
std::string decimal_char = ".";
VerticalTextPosition vertical_text_align;
Timer* scroll_timer = nullptr;
bool mouse_moves_left = false;
bool cursor_blink_visible = true;
unsigned int blink_timer = 0;
int clip_start_offset = 0;
int clip_end_offset = 0;
bool ignore_mouse_events = false;
struct UndoInfo
{
/* set undo text when:
- added char after moving
- destructive block operation (del, cut etc)
- beginning erase
*/
std::string undo_text;
bool first_erase = false;
bool first_text_insert = false;
};
UndoInfo undo_info;
bool select_all_on_focus_gain = true;
static const std::string break_characters;
static const std::string numeric_mode_characters;
};

View file

@ -0,0 +1,19 @@
#pragma once
#include "../../core/widget.h"
#include <vector>
class ListView : public Widget
{
public:
ListView(Widget* parent = nullptr);
void AddItem(const std::string& text);
protected:
void OnPaint(Canvas* canvas) override;
void OnPaintFrame(Canvas* canvas) override;
std::vector<std::string> items;
};

View file

@ -0,0 +1,31 @@
#pragma once
#include "../../core/widget.h"
class Menubar;
class Toolbar;
class Statusbar;
class MainWindow : public Widget
{
public:
MainWindow();
~MainWindow();
Menubar* GetMenubar() const { return MenubarWidget; }
Toolbar* GetToolbar() const { return ToolbarWidget; }
Statusbar* GetStatusbar() const { return StatusbarWidget; }
Widget* GetCentralWidget() const { return CentralWidget; }
void SetCentralWidget(Widget* widget);
protected:
void OnGeometryChanged() override;
private:
Menubar* MenubarWidget = nullptr;
Toolbar* ToolbarWidget = nullptr;
Widget* CentralWidget = nullptr;
Statusbar* StatusbarWidget = nullptr;
};

View file

@ -0,0 +1,14 @@
#pragma once
#include "../../core/widget.h"
class Menubar : public Widget
{
public:
Menubar(Widget* parent);
~Menubar();
protected:
void OnPaint(Canvas* canvas) override;
};

View file

@ -0,0 +1,22 @@
#pragma once
#include "../../core/widget.h"
class PushButton : public Widget
{
public:
PushButton(Widget* parent = nullptr);
void SetText(const std::string& value);
const std::string& GetText() const;
double GetPreferredHeight() const;
protected:
void OnPaintFrame(Canvas* canvas) override;
void OnPaint(Canvas* canvas) override;
private:
std::string text;
};

View file

@ -0,0 +1,97 @@
#pragma once
#include "../../core/widget.h"
#include "../../core/timer.h"
#include <functional>
class Scrollbar : public Widget
{
public:
Scrollbar(Widget* parent);
~Scrollbar();
bool IsVertical() const;
bool IsHorizontal() const;
int GetMin() const;
int GetMax() const;
int GetLineStep() const;
int GetPageStep() const;
int GetPosition() const;
void SetVertical();
void SetHorizontal();
void SetMin(int scroll_min);
void SetMax(int scroll_max);
void SetLineStep(int step);
void SetPageStep(int step);
void SetRanges(int scroll_min, int scroll_max, int line_step, int page_step);
void SetRanges(int view_size, int total_size);
void SetPosition(int pos);
std::function<void()> FuncScroll;
std::function<void()> FuncScrollMin;
std::function<void()> FuncScrollMax;
std::function<void()> FuncScrollLineDecrement;
std::function<void()> FuncScrollLineIncrement;
std::function<void()> FuncScrollPageDecrement;
std::function<void()> FuncScrollPageIncrement;
std::function<void()> FuncScrollThumbRelease;
std::function<void()> FuncScrollThumbTrack;
std::function<void()> FuncScrollEnd;
protected:
void OnMouseMove(const Point& pos) override;
void OnMouseDown(const Point& pos, int key) override;
void OnMouseUp(const Point& pos, int key) override;
void OnMouseLeave() override;
void OnPaint(Canvas* canvas) override;
void OnEnableChanged() override;
void OnGeometryChanged() override;
private:
bool UpdatePartPositions();
int CalculateThumbSize(int track_size);
int CalculateThumbPosition(int thumb_size, int track_size);
Rect CreateRect(int start, int end);
void InvokeScrollEvent(std::function<void()>* event_ptr);
void OnTimerExpired();
bool vertical = false;
int scroll_min = 0;
int scroll_max = 1;
int line_step = 1;
int page_step = 10;
int position = 0;
enum MouseDownMode
{
mouse_down_none,
mouse_down_button_decr,
mouse_down_button_incr,
mouse_down_track_decr,
mouse_down_track_incr,
mouse_down_thumb_drag
} mouse_down_mode = mouse_down_none;
int thumb_start_position = 0;
Point mouse_drag_start_pos;
int thumb_start_pixel_position = 0;
Timer* mouse_down_timer = nullptr;
int last_step_size = 0;
Rect rect_button_decrement;
Rect rect_track_decrement;
Rect rect_thumb;
Rect rect_track_increment;
Rect rect_button_increment;
std::function<void()>* FuncScrollOnMouseDown = nullptr;
static const int decr_height = 16;
static const int incr_height = 16;
};

View file

@ -0,0 +1,19 @@
#pragma once
#include "../../core/widget.h"
class LineEdit;
class Statusbar : public Widget
{
public:
Statusbar(Widget* parent);
~Statusbar();
protected:
void OnPaint(Canvas* canvas) override;
private:
LineEdit* CommandEdit = nullptr;
};

View file

@ -0,0 +1,156 @@
#pragma once
#include "../../core/widget.h"
#include "../../core/timer.h"
#include "../../core/span_layout.h"
#include "../../core/font.h"
#include <functional>
class Scrollbar;
class TextEdit : public Widget
{
public:
TextEdit(Widget* parent);
~TextEdit();
bool IsReadOnly() const;
bool IsLowercase() const;
bool IsUppercase() const;
int GetMaxLength() const;
std::string GetText() const;
int GetLineCount() const;
std::string GetLineText(int line) const;
std::string GetSelection() const;
int GetSelectionStart() const;
int GetSelectionLength() const;
int GetCursorPos() const;
int GetCursorLineNumber() const;
double GetTotalHeight();
void SetSelectAllOnFocusGain(bool enable);
void SelectAll();
void SetReadOnly(bool enable = true);
void SetLowercase(bool enable = true);
void SetUppercase(bool enable = true);
void SetMaxLength(int length);
void SetText(const std::string& text);
void AddText(const std::string& text);
void SetSelection(int pos, int length);
void ClearSelection();
void SetCursorPos(int pos);
void DeleteSelectedText();
void SetInputMask(const std::string& mask);
void SetCursorDrawingEnabled(bool enable);
std::function<std::string(std::string text)> FuncFilterKeyChar;
std::function<void()> FuncBeforeEditChanged;
std::function<void()> FuncAfterEditChanged;
std::function<void()> FuncSelectionChanged;
std::function<void()> FuncFocusGained;
std::function<void()> FuncFocusLost;
std::function<void()> FuncEnterPressed;
protected:
void OnPaintFrame(Canvas* canvas) override;
void OnPaint(Canvas* canvas) override;
void OnMouseMove(const Point& pos) override;
void OnMouseDown(const Point& pos, int key) override;
void OnMouseDoubleclick(const Point& pos, int key) override;
void OnMouseUp(const Point& pos, int key) override;
void OnKeyChar(std::string chars) override;
void OnKeyDown(EInputKey key) override;
void OnKeyUp(EInputKey key) override;
void OnGeometryChanged() override;
void OnEnableChanged() override;
void OnSetFocus() override;
void OnLostFocus() override;
private:
void LayoutLines(Canvas* canvas);
void OnTimerExpired();
void OnScrollTimerExpired();
void CreateComponents();
void OnVerticalScroll();
void UpdateVerticalScroll();
void MoveVerticalScroll();
double GetTotalLineHeight();
struct Line
{
std::string text;
SpanLayout layout;
Rect box;
bool invalidated = true;
};
struct ivec2
{
ivec2() = default;
ivec2(int x, int y) : x(x), y(y) { }
int x = 0;
int y = 0;
bool operator==(const ivec2& b) const { return x == b.x && y == b.y; }
bool operator!=(const ivec2& b) const { return x != b.x || y != b.y; }
};
Scrollbar* vert_scrollbar;
Timer* timer = nullptr;
std::vector<Line> lines = { Line() };
ivec2 cursor_pos = { 0, 0 };
int max_length = -1;
bool mouse_selecting = false;
bool lowercase = false;
bool uppercase = false;
bool readonly = false;
ivec2 selection_start = { -1, 0 };
int selection_length = 0;
std::string input_mask;
static std::string break_characters;
void Move(int steps, bool shift, bool ctrl);
void InsertText(ivec2 pos, const std::string& str);
void Backspace();
void Del();
ivec2 GetCharacterIndex(Point mouse_wincoords);
ivec2 FindNextBreakCharacter(ivec2 pos);
ivec2 FindPreviousBreakCharacter(ivec2 pos);
bool InputMaskAcceptsInput(ivec2 cursor_pos, const std::string& str);
std::string::size_type ToOffset(ivec2 pos) const;
ivec2 FromOffset(std::string::size_type offset) const;
VerticalTextPosition vertical_text_align;
Timer* scroll_timer = nullptr;
bool mouse_moves_left = false;
bool cursor_blink_visible = true;
unsigned int blink_timer = 0;
int clip_start_offset = 0;
int clip_end_offset = 0;
bool ignore_mouse_events = false;
struct UndoInfo
{
/* set undo text when:
- added char after moving
- destructive block operation (del, cut etc)
- beginning erase
*/
std::string undo_text;
bool first_erase = false;
bool first_text_insert = false;
} undo_info;
bool select_all_on_focus_gain = false;
std::shared_ptr<Font> font = Font::Create("Segoe UI", 12.0);
template<typename T>
static T clamp(T val, T minval, T maxval) { return std::max<T>(std::min<T>(val, maxval), minval); }
};

View file

@ -0,0 +1,21 @@
#pragma once
#include "../../core/widget.h"
class TextLabel : public Widget
{
public:
TextLabel(Widget* parent = nullptr);
void SetText(const std::string& value);
const std::string& GetText() const;
double GetPreferredHeight() const;
protected:
void OnPaint(Canvas* canvas) override;
private:
std::string text;
};

View file

@ -0,0 +1,11 @@
#pragma once
#include "../../core/widget.h"
class Toolbar : public Widget
{
public:
Toolbar(Widget* parent);
~Toolbar();
};

View file

@ -0,0 +1,14 @@
#pragma once
#include "../../core/widget.h"
class ToolbarButton : public Widget
{
public:
ToolbarButton(Widget* parent);
~ToolbarButton();
protected:
void OnPaint(Canvas* canvas) override;
};

View file

@ -0,0 +1,173 @@
#pragma once
#include <memory>
#include <string>
#include "../core/rect.h"
class Engine;
enum class StandardCursor
{
arrow,
appstarting,
cross,
hand,
ibeam,
no,
size_all,
size_nesw,
size_ns,
size_nwse,
size_we,
uparrow,
wait
};
enum EInputKey
{
IK_None, IK_LeftMouse, IK_RightMouse, IK_Cancel,
IK_MiddleMouse, IK_Unknown05, IK_Unknown06, IK_Unknown07,
IK_Backspace, IK_Tab, IK_Unknown0A, IK_Unknown0B,
IK_Unknown0C, IK_Enter, IK_Unknown0E, IK_Unknown0F,
IK_Shift, IK_Ctrl, IK_Alt, IK_Pause,
IK_CapsLock, IK_Unknown15, IK_Unknown16, IK_Unknown17,
IK_Unknown18, IK_Unknown19, IK_Unknown1A, IK_Escape,
IK_Unknown1C, IK_Unknown1D, IK_Unknown1E, IK_Unknown1F,
IK_Space, IK_PageUp, IK_PageDown, IK_End,
IK_Home, IK_Left, IK_Up, IK_Right,
IK_Down, IK_Select, IK_Print, IK_Execute,
IK_PrintScrn, IK_Insert, IK_Delete, IK_Help,
IK_0, IK_1, IK_2, IK_3,
IK_4, IK_5, IK_6, IK_7,
IK_8, IK_9, IK_Unknown3A, IK_Unknown3B,
IK_Unknown3C, IK_Unknown3D, IK_Unknown3E, IK_Unknown3F,
IK_Unknown40, IK_A, IK_B, IK_C,
IK_D, IK_E, IK_F, IK_G,
IK_H, IK_I, IK_J, IK_K,
IK_L, IK_M, IK_N, IK_O,
IK_P, IK_Q, IK_R, IK_S,
IK_T, IK_U, IK_V, IK_W,
IK_X, IK_Y, IK_Z, IK_Unknown5B,
IK_Unknown5C, IK_Unknown5D, IK_Unknown5E, IK_Unknown5F,
IK_NumPad0, IK_NumPad1, IK_NumPad2, IK_NumPad3,
IK_NumPad4, IK_NumPad5, IK_NumPad6, IK_NumPad7,
IK_NumPad8, IK_NumPad9, IK_GreyStar, IK_GreyPlus,
IK_Separator, IK_GreyMinus, IK_NumPadPeriod, IK_GreySlash,
IK_F1, IK_F2, IK_F3, IK_F4,
IK_F5, IK_F6, IK_F7, IK_F8,
IK_F9, IK_F10, IK_F11, IK_F12,
IK_F13, IK_F14, IK_F15, IK_F16,
IK_F17, IK_F18, IK_F19, IK_F20,
IK_F21, IK_F22, IK_F23, IK_F24,
IK_Unknown88, IK_Unknown89, IK_Unknown8A, IK_Unknown8B,
IK_Unknown8C, IK_Unknown8D, IK_Unknown8E, IK_Unknown8F,
IK_NumLock, IK_ScrollLock, IK_Unknown92, IK_Unknown93,
IK_Unknown94, IK_Unknown95, IK_Unknown96, IK_Unknown97,
IK_Unknown98, IK_Unknown99, IK_Unknown9A, IK_Unknown9B,
IK_Unknown9C, IK_Unknown9D, IK_Unknown9E, IK_Unknown9F,
IK_LShift, IK_RShift, IK_LControl, IK_RControl,
IK_UnknownA4, IK_UnknownA5, IK_UnknownA6, IK_UnknownA7,
IK_UnknownA8, IK_UnknownA9, IK_UnknownAA, IK_UnknownAB,
IK_UnknownAC, IK_UnknownAD, IK_UnknownAE, IK_UnknownAF,
IK_UnknownB0, IK_UnknownB1, IK_UnknownB2, IK_UnknownB3,
IK_UnknownB4, IK_UnknownB5, IK_UnknownB6, IK_UnknownB7,
IK_UnknownB8, IK_UnknownB9, IK_Semicolon, IK_Equals,
IK_Comma, IK_Minus, IK_Period, IK_Slash,
IK_Tilde, IK_UnknownC1, IK_UnknownC2, IK_UnknownC3,
IK_UnknownC4, IK_UnknownC5, IK_UnknownC6, IK_UnknownC7,
IK_Joy1, IK_Joy2, IK_Joy3, IK_Joy4,
IK_Joy5, IK_Joy6, IK_Joy7, IK_Joy8,
IK_Joy9, IK_Joy10, IK_Joy11, IK_Joy12,
IK_Joy13, IK_Joy14, IK_Joy15, IK_Joy16,
IK_UnknownD8, IK_UnknownD9, IK_UnknownDA, IK_LeftBracket,
IK_Backslash, IK_RightBracket, IK_SingleQuote, IK_UnknownDF,
IK_JoyX, IK_JoyY, IK_JoyZ, IK_JoyR,
IK_MouseX, IK_MouseY, IK_MouseZ, IK_MouseW,
IK_JoyU, IK_JoyV, IK_UnknownEA, IK_UnknownEB,
IK_MouseWheelUp, IK_MouseWheelDown, IK_Unknown10E, IK_Unknown10F,
IK_JoyPovUp, IK_JoyPovDown, IK_JoyPovLeft, IK_JoyPovRight,
IK_UnknownF4, IK_UnknownF5, IK_Attn, IK_CrSel,
IK_ExSel, IK_ErEof, IK_Play, IK_Zoom,
IK_NoName, IK_PA1, IK_OEMClear
};
enum EInputType
{
IST_None,
IST_Press,
IST_Hold,
IST_Release,
IST_Axis
};
class KeyEvent
{
public:
EInputKey Key;
bool Ctrl = false;
bool Alt = false;
bool Shift = false;
Point MousePos = Point(0.0, 0.0);
};
class DisplayWindow;
class DisplayWindowHost
{
public:
virtual void OnWindowPaint() = 0;
virtual void OnWindowMouseMove(const Point& pos) = 0;
virtual void OnWindowMouseDown(const Point& pos, EInputKey key) = 0;
virtual void OnWindowMouseDoubleclick(const Point& pos, EInputKey key) = 0;
virtual void OnWindowMouseUp(const Point& pos, EInputKey key) = 0;
virtual void OnWindowMouseWheel(const Point& pos, EInputKey key) = 0;
virtual void OnWindowRawMouseMove(int dx, int dy) = 0;
virtual void OnWindowKeyChar(std::string chars) = 0;
virtual void OnWindowKeyDown(EInputKey key) = 0;
virtual void OnWindowKeyUp(EInputKey key) = 0;
virtual void OnWindowGeometryChanged() = 0;
virtual void OnWindowClose() = 0;
virtual void OnWindowActivated() = 0;
virtual void OnWindowDeactivated() = 0;
virtual void OnWindowDpiScaleChanged() = 0;
};
class DisplayWindow
{
public:
static std::unique_ptr<DisplayWindow> Create(DisplayWindowHost* windowHost);
static void ProcessEvents();
static void RunLoop();
static void ExitLoop();
virtual ~DisplayWindow() = default;
virtual void SetWindowTitle(const std::string& text) = 0;
virtual void SetWindowFrame(const Rect& box) = 0;
virtual void SetClientFrame(const Rect& box) = 0;
virtual void Show() = 0;
virtual void ShowFullscreen() = 0;
virtual void ShowMaximized() = 0;
virtual void ShowMinimized() = 0;
virtual void ShowNormal() = 0;
virtual void Hide() = 0;
virtual void Activate() = 0;
virtual void ShowCursor(bool enable) = 0;
virtual void LockCursor() = 0;
virtual void UnlockCursor() = 0;
virtual void Update() = 0;
virtual bool GetKeyState(EInputKey key) = 0;
virtual Rect GetWindowFrame() const = 0;
virtual Size GetClientSize() const = 0;
virtual int GetPixelWidth() const = 0;
virtual int GetPixelHeight() const = 0;
virtual double GetDpiScale() const = 0;
virtual void SetBorderColor(uint32_t bgra8) = 0;
virtual void SetCaptionColor(uint32_t bgra8) = 0;
virtual void SetCaptionTextColor(uint32_t bgra8) = 0;
virtual void PresentBitmap(int width, int height, const uint32_t* pixels) = 0;
};