gBar/src/Widget.h

438 lines
9.6 KiB
C
Raw Normal View History

2023-01-13 15:13:56 +00:00
#pragma once
#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
};
struct Transition
2023-01-13 15:13:56 +00:00
{
TransitionType type;
uint32_t durationMS;
};
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;
};
struct Range
2023-01-13 15:13:56 +00:00
{
double min, max;
};
2023-07-28 17:11:13 +00:00
struct SliderRange
{
2023-07-28 17:11:13 +00:00
double min, max;
double step;
2023-01-13 15:13:56 +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>();
}
static void CreateAndAddWidget(Widget* widget, GtkWidget* parentWidget);
2023-01-13 15:13:56 +00:00
void SetClass(const std::string& cssClass);
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);
void RemoveChild(size_t idx);
void RemoveChild(Widget* widget);
std::vector<std::unique_ptr<Widget>>& GetWidgets() { return m_Childs; }
2023-01-13 15:13:56 +00:00
template<typename TWidget>
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;
};
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);
void SetOnCreate(Callback<Widget>&& onCreate) { m_OnCreate = onCreate; }
2023-01-13 15:13:56 +00:00
protected:
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
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:
// 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
};
class CairoArea : public Widget
2023-01-13 15:13:56 +00:00
{
public:
virtual void Create() override;
protected:
virtual void Draw(cairo_t* cr) = 0;
Quad GetQuad();
};
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:
void Draw(cairo_t* cr) override;
2023-01-13 15:13:56 +00:00
double m_Val;
SensorStyle m_Style{};
};
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);
void SetAngle(double angle) { m_Angle = angle; };
private:
void Draw(cairo_t* cr) override;
// These are in percent
double up, down;
Range limitUp;
Range limitDown;
double m_Angle;
// 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;
};
class Texture : public CairoArea
{
public:
Texture() = default;
virtual ~Texture();
// Non-Owning, ARGB32
void SetBuf(size_t width, size_t height, uint8_t* buf);
void ForceHeight(size_t height) { m_ForcedHeight = height; };
void AddPaddingTop(int32_t topPadding) { m_Padding = topPadding; };
void SetAngle(double angle) { m_Angle = angle; }
private:
void Draw(cairo_t* cr) override;
size_t m_Width;
size_t m_Height;
size_t m_ForcedHeight = 0;
double m_Angle;
int32_t m_Padding = 0;
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-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);
void SetAngle(double angle);
2023-01-13 15:13:56 +00:00
virtual void Create() override;
private:
std::string m_Text;
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);
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;
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);
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;
double m_ScrollSpeed = 5. / 100.; // 5%
2023-01-13 15:13:56 +00:00
std::function<void(Slider&, double)> m_OnValueChange;
};
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;
}
}
}