mirror of
https://github.com/scorpion-26/gBar.git
synced 2024-11-22 19:22:50 +00:00
660e690195
Apparently, gtk_button_set_label is very expensive to do. Now it is only called when necessary. This is the culprit for the mysterious high CPU usage and not the Hyprland IPC as said in https://github.com/scorpion-26/gBar/issues/7 and https://github.com/scorpion-26/gBar/issues/8.
619 lines
17 KiB
C++
619 lines
17 KiB
C++
#include "Widget.h"
|
|
#include "Common.h"
|
|
#include "CSS.h"
|
|
|
|
#include <cmath>
|
|
|
|
// TODO: Currently setters only work pre-create. Make them react to changes after creation!
|
|
|
|
namespace Utils
|
|
{
|
|
GtkAlign ToGtkAlign(Alignment align)
|
|
{
|
|
switch (align)
|
|
{
|
|
case Alignment::Left: return GTK_ALIGN_START;
|
|
case Alignment::Right: return GTK_ALIGN_END;
|
|
case Alignment::Center: return GTK_ALIGN_CENTER;
|
|
case Alignment::Fill: return GTK_ALIGN_FILL;
|
|
}
|
|
}
|
|
GtkOrientation ToGtkOrientation(Orientation orientation)
|
|
{
|
|
switch (orientation)
|
|
{
|
|
case Orientation::Vertical: return GTK_ORIENTATION_VERTICAL;
|
|
case Orientation::Horizontal: return GTK_ORIENTATION_HORIZONTAL;
|
|
}
|
|
}
|
|
GtkRevealerTransitionType ToGtkRevealerTransitionType(TransitionType transition)
|
|
{
|
|
switch (transition)
|
|
{
|
|
case TransitionType::Fade: return GTK_REVEALER_TRANSITION_TYPE_CROSSFADE;
|
|
case TransitionType::SlideLeft: return GTK_REVEALER_TRANSITION_TYPE_SLIDE_LEFT;
|
|
case TransitionType::SlideRight: return GTK_REVEALER_TRANSITION_TYPE_SLIDE_RIGHT;
|
|
case TransitionType::SlideDown: return GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN;
|
|
case TransitionType::SlideUp: return GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP;
|
|
}
|
|
}
|
|
}
|
|
|
|
Widget::~Widget()
|
|
{
|
|
m_Childs.clear();
|
|
LOG("Destroy widget");
|
|
gtk_widget_destroy(m_Widget);
|
|
}
|
|
|
|
void Widget::CreateAndAddWidget(Widget* widget, GtkWidget* parentWidget)
|
|
{
|
|
// Create this widget
|
|
widget->Create();
|
|
// Add
|
|
gtk_container_add((GtkContainer*)parentWidget, widget->Get());
|
|
|
|
gtk_widget_show(widget->m_Widget);
|
|
|
|
for (auto& child : widget->GetChilds())
|
|
{
|
|
CreateAndAddWidget(child.get(), widget->Get());
|
|
}
|
|
}
|
|
|
|
void Widget::SetClass(const std::string& cssClass)
|
|
{
|
|
if (m_Widget && m_CssClass != cssClass)
|
|
{
|
|
auto style = gtk_widget_get_style_context(m_Widget);
|
|
gtk_style_context_remove_class(style, m_CssClass.c_str());
|
|
gtk_style_context_add_class(style, cssClass.c_str());
|
|
}
|
|
m_CssClass = cssClass;
|
|
}
|
|
void Widget::AddClass(const std::string& cssClass)
|
|
{
|
|
if (m_Widget)
|
|
{
|
|
auto style = gtk_widget_get_style_context(m_Widget);
|
|
gtk_style_context_add_class(style, cssClass.c_str());
|
|
}
|
|
}
|
|
void Widget::RemoveClass(const std::string& cssClass)
|
|
{
|
|
if (m_Widget)
|
|
{
|
|
auto style = gtk_widget_get_style_context(m_Widget);
|
|
gtk_style_context_remove_class(style, cssClass.c_str());
|
|
}
|
|
}
|
|
|
|
void Widget::SetVerticalTransform(const Transform& transform)
|
|
{
|
|
m_VerticalTransform = transform;
|
|
}
|
|
|
|
void Widget::SetHorizontalTransform(const Transform& transform)
|
|
{
|
|
m_HorizontalTransform = transform;
|
|
}
|
|
|
|
void Widget::SetTooltip(const std::string& tooltip)
|
|
{
|
|
if (m_Widget)
|
|
{
|
|
gtk_widget_set_tooltip_text(m_Widget, tooltip.c_str());
|
|
}
|
|
m_Tooltip = tooltip;
|
|
}
|
|
|
|
void Widget::AddChild(std::unique_ptr<Widget>&& widget)
|
|
{
|
|
if (m_Widget)
|
|
{
|
|
CreateAndAddWidget(widget.get(), m_Widget);
|
|
}
|
|
m_Childs.push_back(std::move(widget));
|
|
}
|
|
|
|
void Widget::RemoveChild(size_t idx)
|
|
{
|
|
ASSERT(idx < m_Childs.size(), "RemoveChild: Invalid index");
|
|
if (m_Widget)
|
|
{
|
|
auto& child = *m_Childs[idx];
|
|
gtk_container_remove((GtkContainer*)child.m_Widget, m_Widget);
|
|
}
|
|
m_Childs.erase(m_Childs.begin() + idx);
|
|
}
|
|
|
|
void Widget::SetVisible(bool visible)
|
|
{
|
|
gtk_widget_set_visible(m_Widget, visible);
|
|
}
|
|
|
|
void Widget::PropagateToParent(GdkEvent* event)
|
|
{
|
|
gtk_propagate_event(gtk_widget_get_parent(m_Widget), event);
|
|
}
|
|
|
|
void Widget::ApplyPropertiesToWidget()
|
|
{
|
|
// Apply style
|
|
auto style = gtk_widget_get_style_context(m_Widget);
|
|
gtk_style_context_add_class(style, m_CssClass.c_str());
|
|
|
|
gtk_widget_set_tooltip_text(m_Widget, m_Tooltip.c_str());
|
|
|
|
// Apply transform
|
|
gtk_widget_set_size_request(m_Widget, m_HorizontalTransform.size, m_VerticalTransform.size);
|
|
gtk_widget_set_halign(m_Widget, Utils::ToGtkAlign(m_HorizontalTransform.alignment));
|
|
gtk_widget_set_valign(m_Widget, Utils::ToGtkAlign(m_VerticalTransform.alignment));
|
|
gtk_widget_set_hexpand(m_Widget, m_HorizontalTransform.expand);
|
|
gtk_widget_set_vexpand(m_Widget, m_VerticalTransform.expand);
|
|
}
|
|
|
|
void Box::SetOrientation(Orientation orientation)
|
|
{
|
|
m_Orientation = orientation;
|
|
}
|
|
|
|
void Box::SetSpacing(Spacing spacing)
|
|
{
|
|
m_Spacing = spacing;
|
|
}
|
|
|
|
void Box::Create()
|
|
{
|
|
m_Widget = gtk_box_new(Utils::ToGtkOrientation(m_Orientation), m_Spacing.free);
|
|
gtk_box_set_homogeneous((GtkBox*)m_Widget, m_Spacing.evenly);
|
|
ApplyPropertiesToWidget();
|
|
}
|
|
|
|
void CenterBox::SetOrientation(Orientation orientation)
|
|
{
|
|
m_Orientation = orientation;
|
|
}
|
|
|
|
void CenterBox::Create()
|
|
{
|
|
ASSERT(m_Childs.size() == 3, "CenterBox needs 3 children!")
|
|
m_Widget = gtk_box_new(Utils::ToGtkOrientation(m_Orientation), 0);
|
|
|
|
gtk_box_pack_start((GtkBox*)m_Widget, m_Childs[0]->Get(), true, true, 0);
|
|
gtk_box_set_center_widget((GtkBox*)m_Widget, m_Childs[1]->Get());
|
|
gtk_box_pack_end((GtkBox*)m_Widget, m_Childs[2]->Get(), true, true, 0);
|
|
|
|
ApplyPropertiesToWidget();
|
|
}
|
|
|
|
void EventBox::SetHoverFn(std::function<void(EventBox&, bool)>&& fn)
|
|
{
|
|
m_HoverFn = fn;
|
|
}
|
|
|
|
void EventBox::SetScrollFn(std::function<void(EventBox&, ScrollDirection)>&& fn)
|
|
{
|
|
m_ScrollFn = fn;
|
|
}
|
|
|
|
void EventBox::Create()
|
|
{
|
|
m_Widget = gtk_event_box_new();
|
|
gtk_event_box_set_above_child((GtkEventBox*)m_Widget, false);
|
|
auto enter = [](GtkWidget*, GdkEventCrossing*, gpointer data) -> gboolean
|
|
{
|
|
EventBox* box = (EventBox*)data;
|
|
if (box->m_HoverFn && box->m_DiffHoverEvents <= 0)
|
|
{
|
|
box->m_HoverFn(*box, true);
|
|
box->m_DiffHoverEvents = 0;
|
|
}
|
|
box->m_DiffHoverEvents++;
|
|
return false;
|
|
};
|
|
auto leave = [](GtkWidget*, GdkEventCrossing*, void* data) -> gboolean
|
|
{
|
|
EventBox* box = (EventBox*)data;
|
|
box->m_DiffHoverEvents--;
|
|
if (box->m_HoverFn && box->m_DiffHoverEvents <= 0)
|
|
{
|
|
box->m_HoverFn(*box, false);
|
|
box->m_DiffHoverEvents = 0;
|
|
}
|
|
return false;
|
|
};
|
|
// I am so done with the GTK docs. The docs clearly say GdkEventScroll and not GdkEventScroll*, but GdkEventScroll* is passed
|
|
auto scroll = [](GtkWidget*, GdkEventScroll* event, void* data) -> gboolean
|
|
{
|
|
EventBox* box = (EventBox*)data;
|
|
if (box->m_ScrollFn)
|
|
{
|
|
if (event->direction == GDK_SCROLL_DOWN)
|
|
{
|
|
box->m_ScrollFn(*box, ScrollDirection::Down);
|
|
}
|
|
else if (event->direction == GDK_SCROLL_UP)
|
|
{
|
|
box->m_ScrollFn(*box, ScrollDirection::Up);
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
gtk_widget_set_events(m_Widget, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_SCROLL_MASK);
|
|
g_signal_connect(m_Widget, "enter-notify-event", G_CALLBACK(+enter), this);
|
|
g_signal_connect(m_Widget, "leave-notify-event", G_CALLBACK(+leave), this);
|
|
g_signal_connect(m_Widget, "scroll-event", G_CALLBACK(+scroll), this);
|
|
|
|
ApplyPropertiesToWidget();
|
|
}
|
|
|
|
void CairoArea::Create()
|
|
{
|
|
m_Widget = gtk_drawing_area_new();
|
|
auto drawFn = [](GtkWidget*, cairo_t* c, void* data) -> gboolean
|
|
{
|
|
CairoArea* area = (CairoArea*)data;
|
|
area->Draw(c);
|
|
return false;
|
|
};
|
|
|
|
g_signal_connect(m_Widget, "draw", G_CALLBACK(+drawFn), this);
|
|
|
|
ApplyPropertiesToWidget();
|
|
}
|
|
|
|
Quad CairoArea::GetQuad()
|
|
{
|
|
GtkAllocation dim;
|
|
gtk_widget_get_allocation(m_Widget, &dim);
|
|
Quad q;
|
|
if (dim.height >= dim.width)
|
|
{
|
|
// Height greater than width; Fill in x and add margin at the top and bottom
|
|
q.size = dim.width;
|
|
q.x = 0;
|
|
q.y = ((double)dim.height - (double)dim.width) / 2;
|
|
}
|
|
else if (dim.width < dim.height)
|
|
{
|
|
// Height greater than width; Fill in y and add margin at the sides
|
|
q.size = dim.height;
|
|
q.y = 0;
|
|
q.x = ((double)dim.width - (double)dim.height) / 2;
|
|
}
|
|
return q;
|
|
}
|
|
|
|
void Sensor::SetValue(double val)
|
|
{
|
|
if (val != m_Val)
|
|
{
|
|
m_Val = val;
|
|
if (m_Widget)
|
|
{
|
|
gtk_widget_queue_draw(m_Widget);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sensor::SetStyle(SensorStyle style)
|
|
{
|
|
m_Style = style;
|
|
}
|
|
|
|
void Sensor::Draw(cairo_t* cr)
|
|
{
|
|
Quad q = GetQuad();
|
|
|
|
double xCenter = q.x + q.size / 2;
|
|
double yCenter = q.y + q.size / 2;
|
|
double radius = (q.size / 2) - (m_Style.strokeWidth / 2);
|
|
|
|
double beg = m_Style.start * (M_PI / 180);
|
|
double angle = m_Val * 2 * M_PI;
|
|
|
|
auto style = gtk_widget_get_style_context(m_Widget);
|
|
GdkRGBA* bgCol;
|
|
GdkRGBA* fgCol;
|
|
gtk_style_context_get(style, GTK_STATE_FLAG_NORMAL, GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &bgCol, NULL);
|
|
gtk_style_context_get(style, GTK_STATE_FLAG_NORMAL, GTK_STYLE_PROPERTY_COLOR, &fgCol, NULL);
|
|
|
|
cairo_set_line_width(cr, m_Style.strokeWidth);
|
|
|
|
// Outer
|
|
cairo_set_source_rgb(cr, bgCol->red, bgCol->green, bgCol->blue);
|
|
cairo_arc(cr, xCenter, yCenter, radius, 0, 2 * M_PI);
|
|
cairo_stroke(cr);
|
|
|
|
// Inner
|
|
cairo_set_source_rgb(cr, fgCol->red, fgCol->green, fgCol->blue);
|
|
cairo_arc(cr, xCenter, yCenter, radius, beg, beg + angle);
|
|
cairo_stroke(cr);
|
|
|
|
gdk_rgba_free(bgCol);
|
|
gdk_rgba_free(fgCol);
|
|
}
|
|
|
|
static std::string NetworkSensorPercentToCSS(double percent)
|
|
{
|
|
if (percent <= 0.)
|
|
{
|
|
return "under";
|
|
}
|
|
else if (percent <= 0.25)
|
|
{
|
|
return "low";
|
|
}
|
|
else if (percent <= 0.50)
|
|
{
|
|
return "mid-low";
|
|
}
|
|
else if (percent <= 0.75)
|
|
{
|
|
return "mid-high";
|
|
}
|
|
else if (percent <= 1.)
|
|
{
|
|
return "high";
|
|
}
|
|
else
|
|
{
|
|
return "over";
|
|
}
|
|
}
|
|
|
|
static double NetworkSensorRateToPercent(double rate, Range range)
|
|
{
|
|
return (rate - range.min) / (range.max - range.min);
|
|
}
|
|
|
|
void NetworkSensor::Create()
|
|
{
|
|
CairoArea::Create();
|
|
|
|
// Add virtual children for style context(I know, it is really gross)
|
|
contextUp = Widget::Create<Box>();
|
|
contextUp->SetSpacing({0, true});
|
|
contextUp->SetClass("network-up-under");
|
|
contextUp->SetHorizontalTransform({0, false, Alignment::Fill});
|
|
contextUp->Create();
|
|
|
|
contextDown = Widget::Create<Box>();
|
|
contextDown->SetSpacing({0, true});
|
|
contextDown->SetClass("network-down-under");
|
|
contextDown->SetHorizontalTransform({0, false, Alignment::Fill});
|
|
contextDown->Create();
|
|
}
|
|
|
|
void NetworkSensor::SetUp(double val)
|
|
{
|
|
if (!contextUp)
|
|
{
|
|
return;
|
|
}
|
|
|
|
up = NetworkSensorRateToPercent(val, limitUp);
|
|
|
|
// Add css class
|
|
std::string newClass = NetworkSensorPercentToCSS(up);
|
|
contextUp->SetClass("network-up-" + newClass);
|
|
|
|
// Schedule redraw
|
|
if (m_Widget)
|
|
{
|
|
gtk_widget_queue_draw(m_Widget);
|
|
}
|
|
}
|
|
|
|
void NetworkSensor::SetDown(double val)
|
|
{
|
|
if (!contextDown)
|
|
{
|
|
return;
|
|
}
|
|
|
|
down = NetworkSensorRateToPercent(val, limitDown);
|
|
|
|
// Add css class
|
|
std::string newClass = NetworkSensorPercentToCSS(down);
|
|
contextDown->SetClass("network-down-" + newClass);
|
|
|
|
// Schedule redraw
|
|
if (m_Widget)
|
|
{
|
|
gtk_widget_queue_draw(m_Widget);
|
|
}
|
|
}
|
|
|
|
void NetworkSensor::Draw(cairo_t* cr)
|
|
{
|
|
constexpr double epsilon = 1;
|
|
|
|
Quad q = GetQuad();
|
|
auto virtToPx = [&](double virtPx)
|
|
{
|
|
return q.size * (virtPx / 24.f);
|
|
};
|
|
|
|
GdkRGBA* colUp;
|
|
GdkRGBA* colDown;
|
|
gtk_style_context_get(gtk_widget_get_style_context(contextUp->Get()), GTK_STATE_FLAG_NORMAL, GTK_STYLE_PROPERTY_COLOR, &colUp, NULL);
|
|
gtk_style_context_get(gtk_widget_get_style_context(contextDown->Get()), GTK_STATE_FLAG_NORMAL, GTK_STYLE_PROPERTY_COLOR, &colDown, NULL);
|
|
|
|
// Upload
|
|
cairo_set_source_rgb(cr, colUp->red, colUp->green, colUp->blue);
|
|
|
|
// Triangle
|
|
cairo_move_to(cr, q.x + virtToPx(6), q.y + virtToPx(0)); // Top mid
|
|
cairo_line_to(cr, q.x + virtToPx(0), q.y + virtToPx(10)); // Left bottom
|
|
cairo_line_to(cr, q.x + virtToPx(12), q.y + virtToPx(10)); // Right bottom
|
|
cairo_close_path(cr);
|
|
cairo_fill(cr);
|
|
|
|
// Rectangle
|
|
// Go a bit above, to avoid gaps between tri and quad
|
|
cairo_rectangle(cr, q.x + virtToPx(4), q.y + virtToPx(10 - epsilon), virtToPx(4), virtToPx(12 + epsilon));
|
|
cairo_fill(cr);
|
|
|
|
// Download
|
|
cairo_set_source_rgb(cr, colDown->red, colDown->green, colDown->blue);
|
|
|
|
// Triangle
|
|
cairo_move_to(cr, q.x + virtToPx(18), q.y + virtToPx(24)); // Bottom mid
|
|
cairo_line_to(cr, q.x + virtToPx(12), q.y + virtToPx(14)); // Left top
|
|
cairo_line_to(cr, q.x + virtToPx(24), q.y + virtToPx(14)); // Right top
|
|
cairo_close_path(cr);
|
|
cairo_fill(cr);
|
|
|
|
// Rectangle
|
|
// Go a bit below, to avoid gaps between tri and quad
|
|
cairo_rectangle(cr, q.x + virtToPx(16), q.y + virtToPx(2), virtToPx(4), virtToPx(12 + epsilon));
|
|
cairo_fill(cr);
|
|
|
|
gdk_rgba_free(colUp);
|
|
gdk_rgba_free(colDown);
|
|
}
|
|
|
|
void Revealer::SetTransition(Transition transition)
|
|
{
|
|
m_Transition = transition;
|
|
}
|
|
|
|
void Revealer::Create()
|
|
{
|
|
m_Widget = gtk_revealer_new();
|
|
gtk_revealer_set_transition_type((GtkRevealer*)m_Widget, Utils::ToGtkRevealerTransitionType(m_Transition.type));
|
|
gtk_revealer_set_transition_duration((GtkRevealer*)m_Widget, m_Transition.durationMS);
|
|
ApplyPropertiesToWidget();
|
|
}
|
|
|
|
void Revealer::SetRevealed(bool revealed)
|
|
{
|
|
gtk_revealer_set_reveal_child((GtkRevealer*)m_Widget, revealed);
|
|
}
|
|
|
|
void Text::SetText(const std::string& text)
|
|
{
|
|
if (m_Widget && text != m_Text)
|
|
{
|
|
gtk_label_set_text((GtkLabel*)m_Widget, m_Text.c_str());
|
|
}
|
|
m_Text = text;
|
|
}
|
|
|
|
void Text::Create()
|
|
{
|
|
m_Widget = gtk_label_new(m_Text.c_str());
|
|
ApplyPropertiesToWidget();
|
|
}
|
|
|
|
void Button::Create()
|
|
{
|
|
m_Widget = gtk_button_new_with_label(m_Text.c_str());
|
|
auto clickFn = [](UNUSED GtkButton* gtkButton, void* data) -> gboolean
|
|
{
|
|
Button* button = (Button*)data;
|
|
if (button->m_OnClick)
|
|
button->m_OnClick(*button);
|
|
return GDK_EVENT_STOP;
|
|
};
|
|
g_signal_connect(m_Widget, "clicked", G_CALLBACK(+clickFn), this);
|
|
ApplyPropertiesToWidget();
|
|
}
|
|
|
|
void Button::SetText(const std::string& text)
|
|
{
|
|
if (m_Widget && text != m_Text)
|
|
{
|
|
gtk_button_set_label((GtkButton*)m_Widget, m_Text.c_str());
|
|
}
|
|
m_Text = text;
|
|
}
|
|
|
|
void Button::OnClick(Callback<Button>&& callback)
|
|
{
|
|
m_OnClick = std::move(callback);
|
|
}
|
|
|
|
void Slider::OnValueChange(std::function<void(Slider&, double)>&& callback)
|
|
{
|
|
m_OnValueChange = callback;
|
|
}
|
|
|
|
void Slider::SetRange(SliderRange range)
|
|
{
|
|
m_Range = range;
|
|
}
|
|
|
|
void Slider::SetOrientation(Orientation orientation)
|
|
{
|
|
m_Orientation = orientation;
|
|
}
|
|
|
|
void Slider::SetValue(double value)
|
|
{
|
|
gtk_range_set_value((GtkRange*)m_Widget, value);
|
|
}
|
|
|
|
void Slider::SetInverted(bool inverted)
|
|
{
|
|
m_Inverted = inverted;
|
|
}
|
|
|
|
void Slider::SetScrollSpeed(double speed)
|
|
{
|
|
m_ScrollSpeed = speed;
|
|
}
|
|
|
|
void Slider::Create()
|
|
{
|
|
m_Widget = gtk_scale_new_with_range(Utils::ToGtkOrientation(m_Orientation), m_Range.min, m_Range.max, m_Range.step);
|
|
gtk_range_set_inverted((GtkRange*)m_Widget, m_Inverted);
|
|
gtk_scale_set_draw_value((GtkScale*)m_Widget, false);
|
|
auto changedFn = [](GtkScale*, GtkScrollType*, double val, void* data)
|
|
{
|
|
Slider* slider = (Slider*)data;
|
|
if (slider->m_OnValueChange)
|
|
slider->m_OnValueChange(*slider, val);
|
|
return false;
|
|
};
|
|
g_signal_connect(m_Widget, "change-value", G_CALLBACK(+changedFn), this);
|
|
|
|
auto scroll = [](GtkWidget*, GdkEventScroll* event, void* data) -> gboolean
|
|
{
|
|
Slider* slider = (Slider*)data;
|
|
double value = gtk_range_get_value((GtkRange*)slider->m_Widget);
|
|
// Range generates a 'smooth' event.
|
|
if (event->delta_y >= 0)
|
|
{
|
|
value -= slider->m_ScrollSpeed;
|
|
slider->SetValue(value);
|
|
if (slider->m_OnValueChange)
|
|
slider->m_OnValueChange(*slider, value);
|
|
}
|
|
else if (event->delta_y <= 0)
|
|
{
|
|
value += slider->m_ScrollSpeed;
|
|
slider->SetValue(value);
|
|
if (slider->m_OnValueChange)
|
|
slider->m_OnValueChange(*slider, value);
|
|
}
|
|
return GDK_EVENT_STOP;
|
|
};
|
|
g_signal_connect(m_Widget, "scroll-event", G_CALLBACK(+scroll), this);
|
|
|
|
// Propagate events to any parent eventboxes
|
|
auto propagate = [](GtkWidget*, GdkEventCrossing* data, gpointer widget) -> gboolean
|
|
{
|
|
Slider* slider = (Slider*)widget;
|
|
// Seems to be necessary. For revealers to work properly, we need to notify it of the event through propagation.
|
|
// Automatic propagation with GDK_EVENT_PROPAGATE doesn't work for some reason
|
|
slider->PropagateToParent((GdkEvent*)data);
|
|
return GDK_EVENT_PROPAGATE;
|
|
};
|
|
gtk_widget_set_events(m_Widget, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_SCROLL_MASK);
|
|
g_signal_connect(m_Widget, "enter-notify-event", G_CALLBACK(+propagate), this);
|
|
g_signal_connect(m_Widget, "leave-notify-event", G_CALLBACK(+propagate), this);
|
|
ApplyPropertiesToWidget();
|
|
}
|