2023-01-13 15:13:56 +00:00
|
|
|
#pragma once
|
2023-07-20 23:08:07 +00:00
|
|
|
#include "Config.h"
|
|
|
|
#include "Log.h"
|
2023-01-13 15:13:56 +00:00
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <vector>
|
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
|
|
|
#include <functional>
|
|
|
|
|
|
|
|
enum class Alignment
|
|
|
|
{
|
|
|
|
Fill,
|
|
|
|
Center,
|
|
|
|
Left,
|
|
|
|
Right,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Transform
|
|
|
|
{
|
|
|
|
int size = -1;
|
|
|
|
bool expand = true;
|
|
|
|
Alignment alignment = Alignment::Fill;
|
2023-07-18 23:00:49 +00:00
|
|
|
// Left/Top
|
|
|
|
int marginBefore = 0;
|
|
|
|
// Right/Bottom
|
|
|
|
int marginAfter = 0;
|
2023-01-13 15:13:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum class Orientation
|
|
|
|
{
|
|
|
|
Vertical,
|
|
|
|
Horizontal
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Spacing
|
|
|
|
{
|
|
|
|
uint32_t free = 0;
|
|
|
|
bool evenly = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum class TransitionType
|
|
|
|
{
|
|
|
|
Fade,
|
|
|
|
SlideLeft,
|
|
|
|
SlideRight,
|
|
|
|
SlideUp,
|
|
|
|
SlideDown
|
|
|
|
};
|
|
|
|
|
2023-01-14 22:18:34 +00:00
|
|
|
struct Transition
|
2023-01-13 15:13:56 +00:00
|
|
|
{
|
|
|
|
TransitionType type;
|
|
|
|
uint32_t durationMS;
|
|
|
|
};
|
|
|
|
|
2023-02-04 15:23:13 +00:00
|
|
|
struct Quad
|
|
|
|
{
|
|
|
|
double x, y, size;
|
|
|
|
};
|
|
|
|
|
2023-01-13 15:13:56 +00:00
|
|
|
struct SensorStyle
|
|
|
|
{
|
|
|
|
double start = -90; // 0 = leftmost; -90 = topmost
|
|
|
|
double strokeWidth = 4;
|
|
|
|
};
|
|
|
|
|
2023-02-04 15:09:08 +00:00
|
|
|
struct Range
|
2023-01-13 15:13:56 +00:00
|
|
|
{
|
2023-02-04 15:09:08 +00:00
|
|
|
double min, max;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SliderRange : Range
|
|
|
|
{
|
|
|
|
double step;
|
2023-01-13 15:13:56 +00:00
|
|
|
};
|
|
|
|
|
2023-01-29 11:23:44 +00:00
|
|
|
enum class TimerDispatchBehaviour
|
|
|
|
{
|
|
|
|
ImmediateDispatch, // Call immediately after adding the timeout, then every x ms
|
|
|
|
LateDispatch // Call for the first time after x ms.
|
|
|
|
};
|
|
|
|
|
2023-01-13 15:13:56 +00:00
|
|
|
enum class TimerResult
|
|
|
|
{
|
|
|
|
Ok,
|
|
|
|
Delete
|
|
|
|
};
|
|
|
|
|
2023-02-12 13:28:46 +00:00
|
|
|
enum class ScrollDirection
|
|
|
|
{
|
|
|
|
Up,
|
|
|
|
Down
|
|
|
|
};
|
|
|
|
|
2023-01-13 15:13:56 +00:00
|
|
|
template<typename TWidget>
|
|
|
|
using Callback = std::function<void(TWidget&)>;
|
|
|
|
template<typename TWidget>
|
|
|
|
using TimerCallback = std::function<TimerResult(TWidget&)>;
|
|
|
|
|
|
|
|
class Widget
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Widget() = default;
|
|
|
|
virtual ~Widget();
|
|
|
|
|
|
|
|
template<typename TWidget>
|
|
|
|
static std::unique_ptr<TWidget> Create()
|
|
|
|
{
|
|
|
|
return std::make_unique<TWidget>();
|
|
|
|
}
|
|
|
|
|
2023-01-14 22:18:34 +00:00
|
|
|
static void CreateAndAddWidget(Widget* widget, GtkWidget* parentWidget);
|
|
|
|
|
2023-01-13 15:13:56 +00:00
|
|
|
void SetClass(const std::string& cssClass);
|
2023-01-14 22:18:34 +00:00
|
|
|
void AddClass(const std::string& cssClass);
|
|
|
|
void RemoveClass(const std::string& cssClass);
|
2023-01-13 15:13:56 +00:00
|
|
|
void SetVerticalTransform(const Transform& transform);
|
|
|
|
void SetHorizontalTransform(const Transform& transform);
|
2023-01-14 13:47:30 +00:00
|
|
|
void SetTooltip(const std::string& tooltip);
|
2023-01-13 15:13:56 +00:00
|
|
|
|
|
|
|
virtual void Create() = 0;
|
|
|
|
|
|
|
|
void AddChild(std::unique_ptr<Widget>&& widget);
|
2023-01-14 22:18:34 +00:00
|
|
|
void RemoveChild(size_t idx);
|
2023-03-17 23:02:40 +00:00
|
|
|
void RemoveChild(Widget* widget);
|
2023-01-14 22:18:34 +00:00
|
|
|
|
2023-01-29 11:23:44 +00:00
|
|
|
std::vector<std::unique_ptr<Widget>>& GetWidgets() { return m_Childs; }
|
2023-01-14 22:18:34 +00:00
|
|
|
|
2023-01-13 15:13:56 +00:00
|
|
|
template<typename TWidget>
|
2023-01-29 11:23:44 +00:00
|
|
|
void AddTimer(TimerCallback<TWidget>&& callback, uint32_t timeoutMS, TimerDispatchBehaviour dispatch = TimerDispatchBehaviour::ImmediateDispatch)
|
2023-01-13 15:13:56 +00:00
|
|
|
{
|
|
|
|
struct TimerPayload
|
|
|
|
{
|
|
|
|
TimerCallback<TWidget> timeoutFn;
|
|
|
|
Widget* thisWidget;
|
|
|
|
};
|
|
|
|
TimerPayload* payload = new TimerPayload();
|
|
|
|
payload->thisWidget = this;
|
|
|
|
payload->timeoutFn = std::move(callback);
|
|
|
|
auto fn = [](void* data) -> int
|
|
|
|
{
|
|
|
|
TimerPayload* payload = (TimerPayload*)data;
|
|
|
|
TimerResult result = payload->timeoutFn(*(TWidget*)payload->thisWidget);
|
|
|
|
if (result == TimerResult::Delete)
|
|
|
|
{
|
|
|
|
delete payload;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
2023-01-29 11:23:44 +00:00
|
|
|
if (dispatch == TimerDispatchBehaviour::ImmediateDispatch)
|
|
|
|
{
|
|
|
|
if (fn(payload) == false)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2023-01-13 15:13:56 +00:00
|
|
|
g_timeout_add(timeoutMS, +fn, payload);
|
|
|
|
}
|
|
|
|
|
|
|
|
GtkWidget* Get() { return m_Widget; };
|
|
|
|
const std::vector<std::unique_ptr<Widget>>& GetChilds() const { return m_Childs; };
|
|
|
|
|
|
|
|
void SetVisible(bool visible);
|
|
|
|
|
2023-04-30 21:13:41 +00:00
|
|
|
void SetOnCreate(Callback<Widget>&& onCreate) { m_OnCreate = onCreate; }
|
|
|
|
|
2023-01-13 15:13:56 +00:00
|
|
|
protected:
|
2023-02-04 14:43:51 +00:00
|
|
|
void PropagateToParent(GdkEvent* event);
|
2023-01-13 15:13:56 +00:00
|
|
|
void ApplyPropertiesToWidget();
|
|
|
|
|
|
|
|
GtkWidget* m_Widget = nullptr;
|
|
|
|
|
|
|
|
std::vector<std::unique_ptr<Widget>> m_Childs;
|
|
|
|
|
|
|
|
std::string m_CssClass;
|
2023-01-14 13:47:30 +00:00
|
|
|
std::string m_Tooltip;
|
2023-01-13 15:13:56 +00:00
|
|
|
Transform m_HorizontalTransform; // X
|
|
|
|
Transform m_VerticalTransform; // Y
|
2023-04-30 21:13:41 +00:00
|
|
|
|
|
|
|
Callback<Widget> m_OnCreate;
|
2023-01-13 15:13:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class Box : public Widget
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Box() = default;
|
|
|
|
virtual ~Box() = default;
|
|
|
|
|
|
|
|
void SetOrientation(Orientation orientation);
|
|
|
|
void SetSpacing(Spacing spacing);
|
|
|
|
|
|
|
|
virtual void Create() override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
Orientation m_Orientation = Orientation::Horizontal;
|
|
|
|
Spacing m_Spacing;
|
|
|
|
};
|
|
|
|
|
|
|
|
class CenterBox : public Widget
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void SetOrientation(Orientation orientation);
|
|
|
|
|
|
|
|
virtual void Create() override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
Orientation m_Orientation = Orientation::Horizontal;
|
|
|
|
};
|
|
|
|
|
|
|
|
class EventBox : public Widget
|
|
|
|
{
|
|
|
|
public:
|
2023-02-12 13:28:46 +00:00
|
|
|
void SetHoverFn(std::function<void(EventBox&, bool)>&& fn);
|
|
|
|
void SetScrollFn(std::function<void(EventBox&, ScrollDirection)>&& fn);
|
2023-01-13 15:13:56 +00:00
|
|
|
virtual void Create() override;
|
|
|
|
|
|
|
|
private:
|
2023-02-22 16:20:30 +00:00
|
|
|
// If two hover events are sent, it needs also two close events for a close.
|
|
|
|
// Somehow not doing that causes an issue.
|
|
|
|
int32_t m_DiffHoverEvents = 0;
|
2023-02-12 13:28:46 +00:00
|
|
|
std::function<void(EventBox&, bool)> m_HoverFn;
|
|
|
|
std::function<void(EventBox&, ScrollDirection)> m_ScrollFn;
|
2023-01-13 15:13:56 +00:00
|
|
|
};
|
|
|
|
|
2023-02-04 15:09:08 +00:00
|
|
|
class CairoArea : public Widget
|
2023-01-13 15:13:56 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual void Create() override;
|
2023-01-29 11:23:44 +00:00
|
|
|
|
2023-02-04 15:09:08 +00:00
|
|
|
protected:
|
|
|
|
virtual void Draw(cairo_t* cr) = 0;
|
2023-02-04 15:23:13 +00:00
|
|
|
|
|
|
|
Quad GetQuad();
|
2023-02-04 15:09:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class Sensor : public CairoArea
|
|
|
|
{
|
|
|
|
public:
|
2023-01-13 15:13:56 +00:00
|
|
|
// Goes from 0-1
|
|
|
|
void SetValue(double val);
|
|
|
|
void SetStyle(SensorStyle style);
|
|
|
|
|
|
|
|
private:
|
2023-02-04 15:09:08 +00:00
|
|
|
void Draw(cairo_t* cr) override;
|
2023-01-13 15:13:56 +00:00
|
|
|
|
|
|
|
double m_Val;
|
|
|
|
SensorStyle m_Style{};
|
|
|
|
};
|
|
|
|
|
2023-02-10 16:20:26 +00:00
|
|
|
class NetworkSensor : public CairoArea
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual void Create() override;
|
|
|
|
|
|
|
|
void SetLimitUp(Range limit) { limitUp = limit; };
|
|
|
|
void SetLimitDown(Range limit) { limitDown = limit; };
|
|
|
|
|
|
|
|
void SetUp(double val);
|
|
|
|
void SetDown(double val);
|
|
|
|
|
2023-07-18 23:04:00 +00:00
|
|
|
void SetAngle(double angle) { m_Angle = angle; };
|
|
|
|
|
2023-02-10 16:20:26 +00:00
|
|
|
private:
|
|
|
|
void Draw(cairo_t* cr) override;
|
|
|
|
|
|
|
|
// These are in percent
|
|
|
|
double up, down;
|
|
|
|
|
|
|
|
Range limitUp;
|
|
|
|
Range limitDown;
|
|
|
|
|
2023-07-18 23:04:00 +00:00
|
|
|
double m_Angle;
|
|
|
|
|
2023-02-10 16:20:26 +00:00
|
|
|
// What I do here is a little bit gross, but I need a working style context
|
|
|
|
// Just manually creating a style context doesn't work for me.
|
|
|
|
std::unique_ptr<Box> contextUp;
|
|
|
|
std::unique_ptr<Box> contextDown;
|
|
|
|
};
|
|
|
|
|
2023-03-17 23:02:40 +00:00
|
|
|
class Texture : public CairoArea
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Texture() = default;
|
|
|
|
virtual ~Texture();
|
|
|
|
|
|
|
|
// Non-Owning, ARGB32
|
|
|
|
void SetBuf(size_t width, size_t height, uint8_t* buf);
|
|
|
|
|
2023-05-04 14:14:18 +00:00
|
|
|
void ForceHeight(size_t height) { m_ForcedHeight = height; };
|
|
|
|
void AddPaddingTop(int32_t topPadding) { m_Padding = topPadding; };
|
2023-07-20 23:08:07 +00:00
|
|
|
void SetAngle(double angle) { m_Angle = angle; }
|
2023-05-04 14:14:18 +00:00
|
|
|
|
2023-03-17 23:02:40 +00:00
|
|
|
private:
|
|
|
|
void Draw(cairo_t* cr) override;
|
|
|
|
|
|
|
|
size_t m_Width;
|
|
|
|
size_t m_Height;
|
2023-05-04 14:14:18 +00:00
|
|
|
size_t m_ForcedHeight = 0;
|
2023-07-20 23:08:07 +00:00
|
|
|
double m_Angle;
|
2023-05-04 14:14:18 +00:00
|
|
|
int32_t m_Padding = 0;
|
2023-03-17 23:02:40 +00:00
|
|
|
GBytes* m_Bytes;
|
|
|
|
GdkPixbuf* m_Pixbuf;
|
|
|
|
};
|
|
|
|
|
2023-01-13 15:13:56 +00:00
|
|
|
class Revealer : public Widget
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void SetTransition(Transition transition);
|
2023-01-29 11:23:44 +00:00
|
|
|
|
2023-01-13 15:13:56 +00:00
|
|
|
void SetRevealed(bool revealed);
|
|
|
|
|
|
|
|
virtual void Create() override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
Transition m_Transition;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Text : public Widget
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Text() = default;
|
|
|
|
virtual ~Text() = default;
|
|
|
|
|
|
|
|
void SetText(const std::string& text);
|
2023-07-18 23:04:00 +00:00
|
|
|
void SetAngle(double angle);
|
2023-01-13 15:13:56 +00:00
|
|
|
|
|
|
|
virtual void Create() override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::string m_Text;
|
2023-07-18 23:04:00 +00:00
|
|
|
double m_Angle;
|
2023-01-13 15:13:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class Button : public Widget
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Button() = default;
|
|
|
|
virtual ~Button() = default;
|
|
|
|
|
|
|
|
void SetText(const std::string& text);
|
2023-07-18 23:04:00 +00:00
|
|
|
void SetAngle(double angle);
|
2023-01-13 15:13:56 +00:00
|
|
|
|
|
|
|
virtual void Create() override;
|
|
|
|
|
|
|
|
void OnClick(Callback<Button>&& callback);
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::string m_Text;
|
2023-07-18 23:04:00 +00:00
|
|
|
double m_Angle;
|
2023-01-13 15:13:56 +00:00
|
|
|
Callback<Button> m_OnClick;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Slider : public Widget
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Slider() = default;
|
|
|
|
virtual ~Slider() = default;
|
|
|
|
|
|
|
|
void SetValue(double value);
|
|
|
|
void SetOrientation(Orientation orientation);
|
|
|
|
void SetInverted(bool flipped);
|
|
|
|
void SetRange(SliderRange range);
|
2023-02-20 22:04:37 +00:00
|
|
|
void SetScrollSpeed(double speed);
|
2023-01-13 15:13:56 +00:00
|
|
|
|
|
|
|
void OnValueChange(std::function<void(Slider&, double)>&& callback);
|
|
|
|
|
|
|
|
virtual void Create() override;
|
|
|
|
|
|
|
|
private:
|
|
|
|
Orientation m_Orientation = Orientation::Horizontal;
|
|
|
|
SliderRange m_Range;
|
|
|
|
bool m_Inverted = false;
|
2023-02-20 22:04:37 +00:00
|
|
|
double m_ScrollSpeed = 5. / 100.; // 5%
|
2023-01-13 15:13:56 +00:00
|
|
|
std::function<void(Slider&, double)> m_OnValueChange;
|
|
|
|
};
|
2023-07-20 23:08:07 +00:00
|
|
|
|
|
|
|
namespace Utils
|
|
|
|
{
|
|
|
|
inline void SetTransform(Widget& widget, const Transform& primary, const Transform& secondary = {})
|
|
|
|
{
|
|
|
|
if (Config::Get().location == 'T' || Config::Get().location == 'B')
|
|
|
|
{
|
|
|
|
widget.SetHorizontalTransform(primary);
|
|
|
|
widget.SetVerticalTransform(secondary);
|
|
|
|
}
|
|
|
|
else if (Config::Get().location == 'R' || Config::Get().location == 'L')
|
|
|
|
{
|
|
|
|
widget.SetVerticalTransform(primary);
|
|
|
|
widget.SetHorizontalTransform(secondary);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Orientation GetOrientation()
|
|
|
|
{
|
|
|
|
switch (Config::Get().location)
|
|
|
|
{
|
|
|
|
case 'T':
|
|
|
|
case 'B': return Orientation::Horizontal;
|
|
|
|
case 'L':
|
|
|
|
case 'R': return Orientation::Vertical;
|
|
|
|
default: LOG("Invalid location char \"" << Config::Get().location << "\"!") return Orientation::Horizontal;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline double GetAngle()
|
|
|
|
{
|
|
|
|
if (Config::Get().location == 'T' || Config::Get().location == 'B')
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if (Config::Get().location == 'L')
|
|
|
|
{
|
|
|
|
return 270; // 90 is buggy (Clipped text)
|
|
|
|
}
|
|
|
|
else if (Config::Get().location == 'R')
|
|
|
|
{
|
|
|
|
return 270;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG("Invalid location char \"" << Config::Get().location << "\"!");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline TransitionType GetTransitionType()
|
|
|
|
{
|
|
|
|
switch (Config::Get().location)
|
|
|
|
{
|
|
|
|
case 'T':
|
|
|
|
case 'B': return TransitionType::SlideLeft;
|
|
|
|
case 'L':
|
|
|
|
case 'R': return TransitionType::SlideUp;
|
|
|
|
default: LOG("Invalid location char \"" << Config::Get().location << "\"!") return TransitionType::SlideLeft;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|