Reputation: 7012
I have a Gtk.Label in an app I am working on migrating from Gtk3 to Gtk4. In Gtk3, Label had a set_angle
method that allowed rotating a label about its center.
That was removed in Gtk4, but there isn't any clear guidance on how to accomplish the same thing.
Following a few comments on various forum threads, I've tried using a CSS transform like
#my-widget {
transform: rotate(90deg);
}
this does rotate the content of the label, but it doens't seem to rotate the size of the label, so the text ends up overlapping other widgets and/or getting truncated if it is near the edge of the window.
I've also tried implementing a custom measure
method that swaps the orientation, and a custom size_allocate
that passes in a rotated transform to gtk_widget_allocate
on the child widget. But then the text doesn't show up at all. I think because it is rotating around the origin instead of the center. Rotating around the center would require knowing the actual size of the widget.
I've also seen suggestions to use pango directly, but that adds significant complexity, and requires calculating the size of the rotated text myself.
Surely there is a simpler way to accomplish this?
I'd really love to see a working example of how to do this.
Upvotes: 0
Views: 53
Reputation: 1020
There are certainly several variants to rotate a widget. In my variant, I created my own class "rotated_label", which I can then use flexibly.
class_rotated_label.h
// class-rotated-label.h
#pragma once
#include<gtk/gtk.h>
#define ROTATED_TYPE_LABEL (rotated_label_get_type())
G_DECLARE_FINAL_TYPE (RotatedLabel, rotated_label, ROTATED, LABEL, GtkWidget)
GtkWidget *rotated_label_new(int angle, const char *text);
class_rotated_label.c
// class-rotated-label.c
#include"class-rotated-label.h"
#include<math.h>
struct _RotatedLabel
{
GtkWidget parent_instance;
GtkWidget *label;
int angle;
int flag;
};
struct _RotatedLabelClass
{
GtkWidgetClass parent_class;
};
G_DEFINE_TYPE (RotatedLabel, rotated_label, GTK_TYPE_WIDGET)
static void
rotated_label_init(RotatedLabel *self)
{
self->label = gtk_label_new("");
self->flag = 0;
gtk_widget_set_parent(GTK_WIDGET(self->label),GTK_WIDGET(self));
// LayoutManager
GtkLayoutManager *center_layout;
center_layout = gtk_widget_get_layout_manager (GTK_WIDGET(self));
gtk_center_layout_set_start_widget (GTK_CENTER_LAYOUT (center_layout),self->label);
}
static void
rotated_label_snapshot (GtkWidget *widget,
GtkSnapshot *snapshot)
{
RotatedLabel *self = ROTATED_LABEL (widget);
int angle = self->angle;
GtkWidget *child;
child = gtk_widget_get_first_child(widget);
int child_width, child_height;
child_width = gtk_widget_get_width (child);
child_height = gtk_widget_get_height(child);
gtk_snapshot_translate(snapshot, &(graphene_point_t){child_width / 2,child_height /2});
gtk_snapshot_rotate (snapshot,angle);
gtk_snapshot_translate(snapshot, &(graphene_point_t){- child_width / 2,- child_height /2.});
gtk_widget_snapshot_child (widget, child, snapshot);
// Calculate the necessary height of the widget
double radiant = angle * (M_PI / 180 );
double sinus = sin(radiant);
double cosinus = cos(radiant);
double result = child_width * ABS(sinus) + child_height * ABS(cosinus);
// only apply once during initialization
if (self->flag == 0)
{
gtk_widget_set_size_request(GTK_WIDGET(self),-1,result);
self->flag = 1;
}
}
static void rotated_label_dispose(GObject *gobject)
{
RotatedLabel *self = ROTATED_LABEL(gobject);
g_clear_pointer(&self->label,gtk_widget_unparent);
G_OBJECT_CLASS (rotated_label_parent_class)->dispose;
}
static void
rotated_label_class_init (RotatedLabelClass *class)
{
G_OBJECT_CLASS(class)->dispose = rotated_label_dispose;
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
widget_class->snapshot = rotated_label_snapshot;
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_CENTER_LAYOUT);
}
GtkWidget *
rotated_label_new (int angle, const char* text)
{
RotatedLabel *self;
self = g_object_new (ROTATED_TYPE_LABEL,NULL);
self->angle = angle;
gtk_label_set_text(GTK_LABEL(self->label),text);
return GTK_WIDGET (self);
}
One possible application could then be as follows:
// rotated-label.c
#include"rotated-label.h"
#include"class-rotated-label.h"
void activate (GtkApplication *app, gpointer data)
{
GtkWidget *window;
window =gtk_application_window_new(app);
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL,0);
GtkWidget *label = rotated_label_new(0,"- first -");
GtkWidget *label1 = rotated_label_new(45,"- second -");
GtkWidget *label2 = rotated_label_new(90,"- third -");
GtkWidget *label3 = rotated_label_new(135,"- fourth -");
GtkWidget *label4 = rotated_label_new(180,"- fifth -");
gtk_box_append(GTK_BOX(box),label);
gtk_box_append(GTK_BOX(box),label1);
gtk_box_append(GTK_BOX(box),label2);
gtk_box_append(GTK_BOX(box),label3);
gtk_box_append(GTK_BOX(box),label4);
gtk_window_set_child(GTK_WINDOW(window),box);
gtk_widget_set_visible(window,TRUE);
}
The result looks like this:
I should perhaps add that I wanted to show just one of the principles. The example is of course still expandable.
Have fun programming.
Upvotes: 0