449 lines
13 KiB
C
449 lines
13 KiB
C
|
#include <gtk/gtk.h>
|
||
|
|
||
|
#define BASELINE_TYPE_WIDGET (baseline_widget_get_type ())
|
||
|
G_DECLARE_FINAL_TYPE (BaselineWidget, baseline_widget, BASELINE, WIDGET, GtkWidget)
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
PROP_ABOVE = 1,
|
||
|
PROP_BELOW,
|
||
|
PROP_ACROSS
|
||
|
};
|
||
|
|
||
|
struct _BaselineWidget
|
||
|
{
|
||
|
GtkWidget parent_instance;
|
||
|
|
||
|
int above, below, across;
|
||
|
};
|
||
|
|
||
|
struct _BaselineWidgetClass
|
||
|
{
|
||
|
GtkWidgetClass parent_class;
|
||
|
};
|
||
|
|
||
|
G_DEFINE_TYPE (BaselineWidget, baseline_widget, GTK_TYPE_WIDGET)
|
||
|
|
||
|
static void
|
||
|
baseline_widget_init (BaselineWidget *self)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
baseline_widget_set_property (GObject *object,
|
||
|
guint prop_id,
|
||
|
const GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
BaselineWidget *self = BASELINE_WIDGET (object);
|
||
|
|
||
|
switch (prop_id)
|
||
|
{
|
||
|
case PROP_ABOVE:
|
||
|
self->above = g_value_get_int (value);
|
||
|
gtk_widget_queue_resize (GTK_WIDGET (object));
|
||
|
break;
|
||
|
|
||
|
case PROP_BELOW:
|
||
|
self->below = g_value_get_int (value);
|
||
|
gtk_widget_queue_resize (GTK_WIDGET (object));
|
||
|
break;
|
||
|
|
||
|
case PROP_ACROSS:
|
||
|
self->across = g_value_get_int (value);
|
||
|
gtk_widget_queue_resize (GTK_WIDGET (object));
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
baseline_widget_get_property (GObject *object,
|
||
|
guint prop_id,
|
||
|
GValue *value,
|
||
|
GParamSpec *pspec)
|
||
|
{
|
||
|
BaselineWidget *self = BASELINE_WIDGET (object);
|
||
|
|
||
|
switch (prop_id)
|
||
|
{
|
||
|
case PROP_ABOVE:
|
||
|
g_value_set_int (value, self->above);
|
||
|
break;
|
||
|
|
||
|
case PROP_BELOW:
|
||
|
g_value_set_int (value, self->below);
|
||
|
break;
|
||
|
|
||
|
case PROP_ACROSS:
|
||
|
g_value_set_int (value, self->across);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
baseline_widget_measure (GtkWidget *widget,
|
||
|
GtkOrientation orientation,
|
||
|
int for_size,
|
||
|
int *minimum,
|
||
|
int *natural,
|
||
|
int *minimum_baseline,
|
||
|
int *natural_baseline)
|
||
|
{
|
||
|
BaselineWidget *self = BASELINE_WIDGET (widget);
|
||
|
|
||
|
if (orientation == GTK_ORIENTATION_VERTICAL)
|
||
|
{
|
||
|
if (self->below >= 0)
|
||
|
{
|
||
|
*minimum = *natural = self->above + self->below;
|
||
|
*minimum_baseline = *natural_baseline = self->above;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*minimum = *natural = self->above;
|
||
|
*minimum_baseline = *natural_baseline = -1;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*minimum = *natural = self->across;
|
||
|
*minimum_baseline = *natural_baseline = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
baseline_widget_snapshot (GtkWidget *widget,
|
||
|
GtkSnapshot *snapshot)
|
||
|
{
|
||
|
BaselineWidget *self = BASELINE_WIDGET (widget);
|
||
|
int height, baseline;
|
||
|
graphene_rect_t bounds;
|
||
|
float widths[4];
|
||
|
GdkRGBA colors[4];
|
||
|
GskRoundedRect outline;
|
||
|
|
||
|
height = gtk_widget_get_height (widget);
|
||
|
baseline = gtk_widget_get_baseline (widget);
|
||
|
|
||
|
gtk_snapshot_save (snapshot);
|
||
|
|
||
|
if (baseline > -1)
|
||
|
{
|
||
|
int y;
|
||
|
if (self->below >= 0)
|
||
|
y = MAX (baseline - self->above, 0);
|
||
|
else
|
||
|
y = CLAMP (baseline - self->above / 2, 0, height - self->above);
|
||
|
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0, y));
|
||
|
}
|
||
|
|
||
|
bounds.origin.x = 0;
|
||
|
bounds.origin.y = 0;
|
||
|
bounds.size.width = self->across;
|
||
|
bounds.size.height = self->above;
|
||
|
|
||
|
if (self->below >= 0)
|
||
|
gtk_snapshot_append_color (snapshot, &(GdkRGBA){1, 1, 0, 0.2}, &bounds);
|
||
|
else
|
||
|
gtk_snapshot_append_color (snapshot, &(GdkRGBA){0, 1, 0, 0.2}, &bounds);
|
||
|
|
||
|
outline = GSK_ROUNDED_RECT_INIT (0, 0, self->across, self->above);
|
||
|
for (int i = 0; i < 4; i++)
|
||
|
{
|
||
|
widths[i] = 1.;
|
||
|
gdk_rgba_parse (&colors[i], "black");
|
||
|
}
|
||
|
|
||
|
gtk_snapshot_append_border (snapshot, &outline, widths, colors);
|
||
|
|
||
|
gtk_snapshot_restore (snapshot);
|
||
|
|
||
|
if (self->below >= 0)
|
||
|
{
|
||
|
gtk_snapshot_save (snapshot);
|
||
|
|
||
|
if (baseline > -1)
|
||
|
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0, baseline));
|
||
|
else
|
||
|
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0, self->above));
|
||
|
bounds.origin.x = 0;
|
||
|
bounds.origin.y = 0;
|
||
|
bounds.size.width = self->across;
|
||
|
bounds.size.height = self->below;
|
||
|
gtk_snapshot_append_color (snapshot, &(GdkRGBA){0, 0, 1, 0.2}, &bounds);
|
||
|
|
||
|
outline = GSK_ROUNDED_RECT_INIT (0, 0, self->across, self->below);
|
||
|
for (int i = 0; i < 4; i++)
|
||
|
{
|
||
|
widths[i] = 1.;
|
||
|
gdk_rgba_parse (&colors[i], "black");
|
||
|
}
|
||
|
|
||
|
gtk_snapshot_append_border (snapshot, &outline, widths, colors);
|
||
|
|
||
|
gtk_snapshot_restore (snapshot);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
static void
|
||
|
baseline_widget_class_init (BaselineWidgetClass *class)
|
||
|
{
|
||
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||
|
|
||
|
object_class->set_property = baseline_widget_set_property;
|
||
|
object_class->get_property = baseline_widget_get_property;
|
||
|
|
||
|
widget_class->snapshot = baseline_widget_snapshot;
|
||
|
widget_class->measure = baseline_widget_measure;
|
||
|
|
||
|
g_object_class_install_property (object_class, PROP_ABOVE,
|
||
|
g_param_spec_int ("above", NULL, NULL, 0, G_MAXINT, 0, G_PARAM_READWRITE));
|
||
|
g_object_class_install_property (object_class, PROP_BELOW,
|
||
|
g_param_spec_int ("below", NULL, NULL, -1, G_MAXINT, 0, G_PARAM_READWRITE));
|
||
|
g_object_class_install_property (object_class, PROP_ACROSS,
|
||
|
g_param_spec_int ("across", NULL, NULL, 0, G_MAXINT, 0, G_PARAM_READWRITE));
|
||
|
}
|
||
|
|
||
|
static GtkWidget *
|
||
|
baseline_widget_new (int above, int below, int across)
|
||
|
{
|
||
|
return g_object_new (BASELINE_TYPE_WIDGET,
|
||
|
"above", above,
|
||
|
"below", below,
|
||
|
"across", across,
|
||
|
"valign", GTK_ALIGN_BASELINE_CENTER,
|
||
|
NULL);
|
||
|
}
|
||
|
|
||
|
int
|
||
|
main (int argc, char *argv[])
|
||
|
{
|
||
|
GtkWidget *window, *header;
|
||
|
GtkWidget *stack, *switcher;
|
||
|
GtkWidget *box, *box1;
|
||
|
GtkWidget *grid;
|
||
|
GtkWidget *child;
|
||
|
|
||
|
gtk_init ();
|
||
|
|
||
|
window = gtk_window_new ();
|
||
|
|
||
|
header = gtk_header_bar_new ();
|
||
|
gtk_window_set_titlebar (GTK_WINDOW (window), header);
|
||
|
|
||
|
stack = gtk_stack_new ();
|
||
|
gtk_window_set_child (GTK_WINDOW (window), stack);
|
||
|
|
||
|
switcher = gtk_stack_switcher_new ();
|
||
|
gtk_stack_switcher_set_stack (GTK_STACK_SWITCHER (switcher), GTK_STACK (stack));
|
||
|
|
||
|
gtk_header_bar_set_title_widget (GTK_HEADER_BAR (header), switcher);
|
||
|
|
||
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 20);
|
||
|
gtk_widget_set_margin_top (box, 20);
|
||
|
gtk_widget_set_margin_bottom (box, 20);
|
||
|
gtk_widget_set_margin_start (box, 20);
|
||
|
gtk_widget_set_margin_end (box, 20);
|
||
|
gtk_widget_set_valign (box, GTK_ALIGN_BASELINE_CENTER);
|
||
|
|
||
|
gtk_stack_add_titled (GTK_STACK (stack), box, "boxes", "Boxes");
|
||
|
|
||
|
box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||
|
gtk_widget_set_halign (box1, GTK_ALIGN_FILL);
|
||
|
gtk_widget_set_valign (box1, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_widget_set_hexpand (box1, TRUE);
|
||
|
|
||
|
child = baseline_widget_new (20, 10, 20);
|
||
|
gtk_box_append (GTK_BOX (box1), child);
|
||
|
|
||
|
child = baseline_widget_new (5, 20, 20);
|
||
|
gtk_box_append (GTK_BOX (box1), child);
|
||
|
|
||
|
child = baseline_widget_new (25, -1, 20);
|
||
|
gtk_box_append (GTK_BOX (box1), child);
|
||
|
|
||
|
child = baseline_widget_new (25, 20, 30);
|
||
|
gtk_box_append (GTK_BOX (box1), child);
|
||
|
|
||
|
gtk_box_append (GTK_BOX (box), box1);
|
||
|
|
||
|
box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||
|
gtk_widget_set_halign (box1, GTK_ALIGN_FILL);
|
||
|
gtk_widget_set_valign (box1, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_widget_set_hexpand (box1, TRUE);
|
||
|
|
||
|
child = baseline_widget_new (10, 15, 10);
|
||
|
gtk_box_append (GTK_BOX (box1), child);
|
||
|
|
||
|
child = baseline_widget_new (80, -1, 20);
|
||
|
gtk_box_append (GTK_BOX (box1), child);
|
||
|
|
||
|
child = baseline_widget_new (60, 15, 20);
|
||
|
gtk_box_append (GTK_BOX (box1), child);
|
||
|
|
||
|
child = baseline_widget_new (5, 10, 30);
|
||
|
gtk_box_append (GTK_BOX (box1), child);
|
||
|
|
||
|
gtk_box_append (GTK_BOX (box), box1);
|
||
|
|
||
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 20);
|
||
|
gtk_widget_set_margin_top (box, 20);
|
||
|
gtk_widget_set_margin_bottom (box, 20);
|
||
|
gtk_widget_set_margin_start (box, 20);
|
||
|
gtk_widget_set_margin_end (box, 20);
|
||
|
gtk_widget_set_valign (box, GTK_ALIGN_BASELINE_CENTER);
|
||
|
|
||
|
grid = gtk_grid_new ();
|
||
|
gtk_widget_set_valign (grid, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_widget_set_hexpand (grid, TRUE);
|
||
|
|
||
|
child = baseline_widget_new (20, 10, 20);
|
||
|
gtk_grid_attach (GTK_GRID (grid), child, 0, 0, 1, 1);
|
||
|
|
||
|
child = baseline_widget_new (5, 20, 20);
|
||
|
gtk_grid_attach (GTK_GRID (grid), child, 1, 0, 1, 1);
|
||
|
|
||
|
child = baseline_widget_new (25, -1, 20);
|
||
|
gtk_grid_attach (GTK_GRID (grid), child, 0, 1, 1, 1);
|
||
|
|
||
|
child = baseline_widget_new (25, 20, 30);
|
||
|
gtk_grid_attach (GTK_GRID (grid), child, 1, 1, 1, 1);
|
||
|
|
||
|
gtk_box_append (GTK_BOX (box), grid);
|
||
|
|
||
|
grid = gtk_grid_new ();
|
||
|
gtk_widget_set_valign (grid, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_widget_set_hexpand (grid, TRUE);
|
||
|
|
||
|
child = baseline_widget_new (10, 15, 10);
|
||
|
gtk_grid_attach (GTK_GRID (grid), child, 0, 0, 1, 1);
|
||
|
|
||
|
child = baseline_widget_new (80, -1, 20);
|
||
|
gtk_grid_attach (GTK_GRID (grid), child, 1, 0, 1, 1);
|
||
|
|
||
|
child = baseline_widget_new (60, 15, 20);
|
||
|
gtk_grid_attach (GTK_GRID (grid), child, 0, 1, 1, 1);
|
||
|
|
||
|
child = baseline_widget_new (5, 10, 30);
|
||
|
gtk_grid_attach (GTK_GRID (grid), child, 1, 1, 1, 1);
|
||
|
|
||
|
gtk_box_append (GTK_BOX (box), grid);
|
||
|
|
||
|
gtk_stack_add_titled (GTK_STACK (stack), box, "grids", "Grids");
|
||
|
|
||
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 20);
|
||
|
gtk_widget_set_margin_top (box, 20);
|
||
|
gtk_widget_set_margin_bottom (box, 20);
|
||
|
gtk_widget_set_margin_start (box, 20);
|
||
|
gtk_widget_set_margin_end (box, 20);
|
||
|
gtk_widget_set_valign (box, GTK_ALIGN_BASELINE_CENTER);
|
||
|
|
||
|
child = baseline_widget_new (60, 15, 20);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_label_new ("Label");
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_entry_new ();
|
||
|
gtk_editable_set_text (GTK_EDITABLE (child), "Entry");
|
||
|
gtk_editable_set_width_chars (GTK_EDITABLE (child), 10);
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_password_entry_new ();
|
||
|
gtk_editable_set_text (GTK_EDITABLE (child), "Password");
|
||
|
gtk_editable_set_width_chars (GTK_EDITABLE (child), 10);
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_spin_button_new_with_range (0, 100, 1);
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_spin_button_new_with_range (0, 100, 1);
|
||
|
gtk_orientable_set_orientation (GTK_ORIENTABLE (child), GTK_ORIENTATION_VERTICAL);
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_switch_new ();
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, 100, 1);
|
||
|
gtk_widget_set_size_request (child, 100, -1);
|
||
|
//gtk_scale_add_mark (GTK_SCALE (child), 50, GTK_POS_BOTTOM, "50");
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
gtk_stack_add_titled (GTK_STACK (stack), box, "controls", "Controls");
|
||
|
|
||
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 20);
|
||
|
gtk_widget_set_margin_top (box, 20);
|
||
|
gtk_widget_set_margin_bottom (box, 20);
|
||
|
gtk_widget_set_margin_start (box, 20);
|
||
|
gtk_widget_set_margin_end (box, 20);
|
||
|
gtk_widget_set_valign (box, GTK_ALIGN_BASELINE_CENTER);
|
||
|
|
||
|
child = baseline_widget_new (60, 15, 20);
|
||
|
gtk_widget_set_hexpand (child, TRUE);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_label_new ("Label");
|
||
|
gtk_widget_set_hexpand (child, TRUE);
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_label_new ("Two\nlines");
|
||
|
gtk_widget_set_hexpand (child, TRUE);
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_label_new ("<span size='large'>Large</span>");
|
||
|
gtk_widget_set_hexpand (child, TRUE);
|
||
|
gtk_label_set_use_markup (GTK_LABEL (child), TRUE);
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_label_new ("<span size='xx-large'>Huge</span>");
|
||
|
gtk_widget_set_hexpand (child, TRUE);
|
||
|
gtk_label_set_use_markup (GTK_LABEL (child), TRUE);
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_label_new ("<span underline='double'>Underlined</span>");
|
||
|
gtk_widget_set_hexpand (child, TRUE);
|
||
|
gtk_label_set_use_markup (GTK_LABEL (child), TRUE);
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_label_new ("♥️");
|
||
|
gtk_widget_set_hexpand (child, TRUE);
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
child = gtk_image_new_from_icon_name ("edit-copy-symbolic");
|
||
|
gtk_widget_set_hexpand (child, TRUE);
|
||
|
gtk_widget_set_valign (child, GTK_ALIGN_BASELINE_CENTER);
|
||
|
gtk_box_append (GTK_BOX (box), child);
|
||
|
|
||
|
gtk_stack_add_titled (GTK_STACK (stack), box, "labels", "Labels");
|
||
|
|
||
|
gtk_window_present (GTK_WINDOW (window));
|
||
|
|
||
|
while (g_list_model_get_n_items (gtk_window_get_toplevels ()) > 0)
|
||
|
g_main_context_iteration (NULL, FALSE);
|
||
|
|
||
|
return 0;
|
||
|
}
|