2017-02-09 16:59:24 +00:00
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct _GtkFocusWidget GtkFocusWidget;
|
|
|
|
|
typedef struct _GtkFocusWidgetClass GtkFocusWidgetClass;
|
|
|
|
|
|
|
|
|
|
#define GTK_TYPE_FOCUS_WIDGET (gtk_focus_widget_get_type ())
|
|
|
|
|
#define GTK_FOCUS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, GTK_TYPE_FOCUS_WIDGET, GtkFocusWidget))
|
|
|
|
|
#define GTK_FOCUS_WIDGET_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST(cls, GTK_TYPE_FOCUS_WIDGET, GtkFocusWidgetClass))
|
|
|
|
|
#define GTK_IS_FOCUS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GTK_TYPE_FOCUS_WIDGET))
|
|
|
|
|
#define GTK_IS_FOCUS_WIDGET_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE(cls, GTK_TYPE_FOCUS_WIDGET))
|
|
|
|
|
#define GTK_FOCUS_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS(obj, GTK_TYPE_FOCUS_WIDGET, GtkFocusWidgetClass))
|
|
|
|
|
|
2017-06-02 20:08:22 +00:00
|
|
|
|
const char *css =
|
|
|
|
|
"* {"
|
|
|
|
|
" transition: none; "
|
|
|
|
|
"}"
|
|
|
|
|
"focuswidget {"
|
|
|
|
|
" padding: 30px;"
|
|
|
|
|
" font-size: 70%;"
|
|
|
|
|
"}"
|
|
|
|
|
"focuswidget button:nth-child(1) {"
|
|
|
|
|
" margin-right: 15px;"
|
|
|
|
|
" margin-bottom: 15px;"
|
|
|
|
|
"}"
|
|
|
|
|
"focuswidget button:nth-child(2) {"
|
|
|
|
|
" margin-left: 15px;"
|
|
|
|
|
" margin-bottom: 15px;"
|
|
|
|
|
"}"
|
|
|
|
|
"focuswidget button:nth-child(3) {"
|
|
|
|
|
" margin-right: 15px;"
|
|
|
|
|
" margin-top: 15px;"
|
|
|
|
|
"}"
|
|
|
|
|
"focuswidget button:nth-child(4) {"
|
|
|
|
|
" margin-left: 15px;"
|
|
|
|
|
" margin-top: 15px;"
|
|
|
|
|
"}"
|
|
|
|
|
"focuswidget button {"
|
|
|
|
|
" min-width: 80px;"
|
|
|
|
|
" min-height: 80px;"
|
|
|
|
|
" margin: 0px;"
|
|
|
|
|
" border: 5px solid green;"
|
|
|
|
|
" border-radius: 0px;"
|
|
|
|
|
" padding: 10px;"
|
|
|
|
|
" background-image: none;"
|
|
|
|
|
" background-color: white;"
|
|
|
|
|
" box-shadow: none;"
|
|
|
|
|
"}"
|
2020-04-16 03:09:36 +00:00
|
|
|
|
"focuswidget button:focus-visible {"
|
2017-11-01 11:47:11 +00:00
|
|
|
|
" outline-width: 4px;"
|
|
|
|
|
" outline-color: yellow;"
|
|
|
|
|
"}"
|
2017-06-02 20:08:22 +00:00
|
|
|
|
"focuswidget button:hover {"
|
|
|
|
|
" background-color: black;"
|
|
|
|
|
" color: white;"
|
|
|
|
|
"}"
|
|
|
|
|
"focuswidget button label:hover {"
|
|
|
|
|
" background-color: green;"
|
|
|
|
|
"}"
|
|
|
|
|
;
|
2017-02-09 16:59:24 +00:00
|
|
|
|
|
|
|
|
|
struct _GtkFocusWidget
|
|
|
|
|
{
|
|
|
|
|
GtkWidget parent_instance;
|
2018-03-07 14:14:37 +00:00
|
|
|
|
double mouse_x;
|
|
|
|
|
double mouse_y;
|
2017-02-09 16:59:24 +00:00
|
|
|
|
|
|
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
GtkWidget *child1;
|
|
|
|
|
GtkWidget *child2;
|
|
|
|
|
GtkWidget *child3;
|
|
|
|
|
GtkWidget *child4;
|
|
|
|
|
};
|
|
|
|
|
GtkWidget* children[4];
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct _GtkFocusWidgetClass
|
|
|
|
|
{
|
|
|
|
|
GtkWidgetClass parent_class;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
GType gtk_focus_widget_get_type (void) G_GNUC_CONST;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE(GtkFocusWidget, gtk_focus_widget, GTK_TYPE_WIDGET)
|
|
|
|
|
|
|
|
|
|
static void
|
2018-08-16 04:53:03 +00:00
|
|
|
|
gtk_focus_widget_size_allocate (GtkWidget *widget,
|
|
|
|
|
int width,
|
|
|
|
|
int height,
|
|
|
|
|
int baseline)
|
2017-02-09 16:59:24 +00:00
|
|
|
|
{
|
|
|
|
|
GtkFocusWidget *self = GTK_FOCUS_WIDGET (widget);
|
2018-08-16 04:53:03 +00:00
|
|
|
|
int child_width = width / 2;
|
|
|
|
|
int child_height = height / 2;
|
2017-02-09 16:59:24 +00:00
|
|
|
|
GtkAllocation child_alloc;
|
|
|
|
|
|
2017-06-02 20:08:22 +00:00
|
|
|
|
child_alloc.x = 0;
|
|
|
|
|
child_alloc.y = 0;
|
2017-02-09 16:59:24 +00:00
|
|
|
|
child_alloc.width = child_width;
|
|
|
|
|
child_alloc.height = child_height;
|
|
|
|
|
|
2018-03-31 19:02:28 +00:00
|
|
|
|
gtk_widget_size_allocate (self->child1, &child_alloc, -1);
|
2017-02-09 16:59:24 +00:00
|
|
|
|
|
2017-06-02 20:08:22 +00:00
|
|
|
|
child_alloc.x += child_width;
|
2017-02-09 16:59:24 +00:00
|
|
|
|
|
2018-03-31 19:02:28 +00:00
|
|
|
|
gtk_widget_size_allocate (self->child2, &child_alloc, -1);
|
2017-02-09 16:59:24 +00:00
|
|
|
|
|
2017-06-02 20:08:22 +00:00
|
|
|
|
child_alloc.y += child_height;
|
2017-02-09 16:59:24 +00:00
|
|
|
|
|
2018-03-31 19:02:28 +00:00
|
|
|
|
gtk_widget_size_allocate (self->child4, &child_alloc, -1);
|
2017-02-09 16:59:24 +00:00
|
|
|
|
|
2017-06-02 20:08:22 +00:00
|
|
|
|
child_alloc.x -= child_width;
|
2017-02-09 16:59:24 +00:00
|
|
|
|
|
2018-03-31 19:02:28 +00:00
|
|
|
|
gtk_widget_size_allocate (self->child3, &child_alloc, -1);
|
2017-02-09 16:59:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_focus_widget_measure (GtkWidget *widget,
|
|
|
|
|
GtkOrientation orientation,
|
|
|
|
|
int for_size,
|
|
|
|
|
int *minimum,
|
|
|
|
|
int *natural,
|
|
|
|
|
int *minimum_baseline,
|
|
|
|
|
int *natural_baseline)
|
|
|
|
|
{
|
|
|
|
|
GtkFocusWidget *self = GTK_FOCUS_WIDGET (widget);
|
|
|
|
|
int min, nat;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
*minimum = 0;
|
|
|
|
|
*natural = 0;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i ++)
|
|
|
|
|
{
|
|
|
|
|
gtk_widget_measure (self->children[i], orientation, for_size,
|
|
|
|
|
&min, &nat, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
*minimum = MAX (*minimum, min);
|
|
|
|
|
*natural = MAX (*natural, nat);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*minimum *= 2;
|
|
|
|
|
*natural *= 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_focus_widget_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
|
|
|
|
|
{
|
|
|
|
|
GtkFocusWidget *self = GTK_FOCUS_WIDGET (widget);
|
|
|
|
|
|
|
|
|
|
gtk_widget_snapshot_child (widget, self->child1, snapshot);
|
|
|
|
|
gtk_widget_snapshot_child (widget, self->child2, snapshot);
|
|
|
|
|
gtk_widget_snapshot_child (widget, self->child3, snapshot);
|
|
|
|
|
gtk_widget_snapshot_child (widget, self->child4, snapshot);
|
2017-06-02 20:08:22 +00:00
|
|
|
|
|
|
|
|
|
if (self->mouse_x != G_MININT && self->mouse_y != G_MININT)
|
|
|
|
|
{
|
|
|
|
|
PangoLayout *layout;
|
|
|
|
|
char *text;
|
|
|
|
|
GtkAllocation alloc;
|
|
|
|
|
graphene_rect_t bounds;
|
|
|
|
|
GdkRGBA black = {0, 0, 0, 1};
|
|
|
|
|
|
|
|
|
|
gtk_widget_get_allocation (widget, &alloc);
|
|
|
|
|
|
|
|
|
|
/* Since event coordinates and drawing is supposed to happen in the
|
2020-05-28 08:00:03 +00:00
|
|
|
|
* same coordinates space, this should all work out just fine. */
|
2017-06-02 20:08:22 +00:00
|
|
|
|
bounds.origin.x = self->mouse_x;
|
|
|
|
|
bounds.origin.y = -30;
|
|
|
|
|
bounds.size.width = 1;
|
|
|
|
|
bounds.size.height = alloc.height;
|
|
|
|
|
gtk_snapshot_append_color (snapshot,
|
|
|
|
|
&black,
|
2018-04-24 01:17:23 +00:00
|
|
|
|
&bounds);
|
2017-06-02 20:08:22 +00:00
|
|
|
|
|
|
|
|
|
bounds.origin.x = -30;
|
|
|
|
|
bounds.origin.y = self->mouse_y;
|
|
|
|
|
bounds.size.width = alloc.width;
|
|
|
|
|
bounds.size.height = 1;
|
|
|
|
|
gtk_snapshot_append_color (snapshot,
|
|
|
|
|
&black,
|
2018-04-24 01:17:23 +00:00
|
|
|
|
&bounds);
|
2017-06-02 20:08:22 +00:00
|
|
|
|
|
|
|
|
|
layout = gtk_widget_create_pango_layout (widget, NULL);
|
2018-03-07 14:14:37 +00:00
|
|
|
|
text = g_strdup_printf ("%.2f×%.2f", self->mouse_x, self->mouse_y);
|
2017-06-02 20:08:22 +00:00
|
|
|
|
pango_layout_set_text (layout, text, -1);
|
|
|
|
|
|
|
|
|
|
gtk_snapshot_render_layout (snapshot,
|
|
|
|
|
gtk_widget_get_style_context (widget),
|
|
|
|
|
self->mouse_x + 2,
|
|
|
|
|
self->mouse_y - 15, /* *shrug* */
|
|
|
|
|
layout);
|
|
|
|
|
|
|
|
|
|
g_free (text);
|
|
|
|
|
g_object_unref (layout);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-30 11:13:24 +00:00
|
|
|
|
static void
|
|
|
|
|
motion_cb (GtkEventControllerMotion *controller,
|
|
|
|
|
gdouble x,
|
|
|
|
|
gdouble y,
|
|
|
|
|
GtkWidget *widget)
|
2017-06-02 20:08:22 +00:00
|
|
|
|
{
|
|
|
|
|
GtkFocusWidget *self = GTK_FOCUS_WIDGET (widget);
|
2017-08-28 23:11:14 +00:00
|
|
|
|
|
2018-07-30 11:13:24 +00:00
|
|
|
|
self->mouse_x = x;
|
|
|
|
|
self->mouse_y = y;
|
2017-06-02 20:08:22 +00:00
|
|
|
|
|
2018-07-30 11:13:24 +00:00
|
|
|
|
gtk_widget_queue_draw (widget);
|
2017-02-09 16:59:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_focus_widget_finalize (GObject *object)
|
|
|
|
|
{
|
|
|
|
|
GtkFocusWidget *self = GTK_FOCUS_WIDGET (object);
|
|
|
|
|
|
|
|
|
|
gtk_widget_unparent (self->child1);
|
|
|
|
|
gtk_widget_unparent (self->child2);
|
|
|
|
|
gtk_widget_unparent (self->child3);
|
|
|
|
|
gtk_widget_unparent (self->child4);
|
|
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (gtk_focus_widget_parent_class)->finalize (object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_focus_widget_init (GtkFocusWidget *self)
|
|
|
|
|
{
|
2018-07-30 11:13:24 +00:00
|
|
|
|
GtkEventController *controller;
|
|
|
|
|
|
2017-02-09 16:59:24 +00:00
|
|
|
|
self->child1 = gtk_button_new_with_label ("1");
|
|
|
|
|
gtk_widget_set_parent (self->child1, GTK_WIDGET (self));
|
|
|
|
|
self->child2 = gtk_button_new_with_label ("2");
|
|
|
|
|
gtk_widget_set_parent (self->child2, GTK_WIDGET (self));
|
|
|
|
|
self->child3 = gtk_button_new_with_label ("3");
|
|
|
|
|
gtk_widget_set_parent (self->child3, GTK_WIDGET (self));
|
|
|
|
|
self->child4 = gtk_button_new_with_label ("4");
|
|
|
|
|
gtk_widget_set_parent (self->child4, GTK_WIDGET (self));
|
2017-06-02 20:08:22 +00:00
|
|
|
|
|
|
|
|
|
self->mouse_x = G_MININT;
|
|
|
|
|
self->mouse_y = G_MININT;
|
2018-07-30 11:13:24 +00:00
|
|
|
|
|
|
|
|
|
controller = gtk_event_controller_motion_new ();
|
|
|
|
|
g_signal_connect (controller, "motion",
|
|
|
|
|
G_CALLBACK (motion_cb), self);
|
|
|
|
|
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
2017-02-09 16:59:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_focus_widget_class_init (GtkFocusWidgetClass *klass)
|
|
|
|
|
{
|
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
|
|
|
|
|
|
object_class->finalize = gtk_focus_widget_finalize;
|
|
|
|
|
|
|
|
|
|
widget_class->snapshot = gtk_focus_widget_snapshot;
|
|
|
|
|
widget_class->measure = gtk_focus_widget_measure;
|
|
|
|
|
widget_class->size_allocate = gtk_focus_widget_size_allocate;
|
2017-06-02 20:08:22 +00:00
|
|
|
|
|
|
|
|
|
gtk_widget_class_set_css_name (widget_class, "focuswidget");
|
2017-02-09 16:59:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-02-10 03:24:47 +00:00
|
|
|
|
static void
|
|
|
|
|
quit_cb (GtkWidget *widget,
|
2020-03-06 17:05:43 +00:00
|
|
|
|
gpointer user_data)
|
2020-02-10 03:24:47 +00:00
|
|
|
|
{
|
2020-03-06 17:05:43 +00:00
|
|
|
|
gboolean *is_done = user_data;
|
2020-02-10 03:24:47 +00:00
|
|
|
|
|
2020-03-06 17:05:43 +00:00
|
|
|
|
*is_done = TRUE;
|
2020-02-10 03:24:47 +00:00
|
|
|
|
|
|
|
|
|
g_main_context_wakeup (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-09 16:59:24 +00:00
|
|
|
|
int
|
2020-03-06 17:05:43 +00:00
|
|
|
|
main(int argc, char **argv)
|
2017-02-09 16:59:24 +00:00
|
|
|
|
{
|
|
|
|
|
GtkWidget *window;
|
|
|
|
|
GtkWidget *widget;
|
2017-06-02 20:08:22 +00:00
|
|
|
|
GtkCssProvider *provider;
|
2020-02-10 03:24:47 +00:00
|
|
|
|
gboolean done = FALSE;
|
2017-06-02 20:08:22 +00:00
|
|
|
|
|
2017-02-09 16:59:24 +00:00
|
|
|
|
gtk_init ();
|
|
|
|
|
|
2017-06-02 20:08:22 +00:00
|
|
|
|
provider = gtk_css_provider_new ();
|
|
|
|
|
gtk_css_provider_load_from_data (provider, css, -1);
|
2017-10-31 01:35:21 +00:00
|
|
|
|
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
|
|
|
|
|
GTK_STYLE_PROVIDER (provider),
|
|
|
|
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
2017-02-09 16:59:24 +00:00
|
|
|
|
|
2020-02-14 19:55:36 +00:00
|
|
|
|
window = gtk_window_new ();
|
2017-02-09 16:59:24 +00:00
|
|
|
|
widget = g_object_new (GTK_TYPE_FOCUS_WIDGET, NULL);
|
|
|
|
|
|
2017-06-02 20:08:22 +00:00
|
|
|
|
gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
|
|
|
|
|
|
2020-05-02 21:26:54 +00:00
|
|
|
|
gtk_window_set_child (GTK_WINDOW (window), widget);
|
2020-02-10 03:24:47 +00:00
|
|
|
|
g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done);
|
2017-02-09 16:59:24 +00:00
|
|
|
|
|
|
|
|
|
gtk_widget_show (window);
|
|
|
|
|
|
2020-02-10 03:24:47 +00:00
|
|
|
|
while (!done)
|
|
|
|
|
g_main_context_iteration (NULL, TRUE);
|
2017-02-09 16:59:24 +00:00
|
|
|
|
}
|