diff options
| author | Mattias Andrée <m@maandree.se> | 2026-10-11 23:01:38 +0200 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2026-10-11 23:29:28 +0200 |
| commit | 94659550993f454e12e4bcb8cf93a5ff258bc08c (patch) | |
| tree | f8e1cfe9fbcdec6f09b6cc18c761684dc34505d2 /logical-scroll-pane.c | |
| parent | First commit (diff) | |
| download | gbild-94659550993f454e12e4bcb8cf93a5ff258bc08c.tar.gz gbild-94659550993f454e12e4bcb8cf93a5ff258bc08c.tar.bz2 gbild-94659550993f454e12e4bcb8cf93a5ff258bc08c.tar.xz | |
Second commit
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'logical-scroll-pane.c')
| -rw-r--r-- | logical-scroll-pane.c | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/logical-scroll-pane.c b/logical-scroll-pane.c new file mode 100644 index 0000000..394cfb6 --- /dev/null +++ b/logical-scroll-pane.c @@ -0,0 +1,430 @@ +#include "common.h" + +G_DEFINE_TYPE(LogicalScrollPane, logical_scroll_pane, GTK_TYPE_BIN) + +static void +logical_scroll_pane_queue_child_draw(GtkAdjustment *adjustment, LogicalScrollPane *this) +{ + (void)adjustment; + if (this->child) + gtk_widget_queue_draw(this->child); +} + +static void +logical_scroll_pane_configure_adjustment(GtkAdjustment *adjustment, int content_size, int page_size) +{ + double value, limit; + + page_size = MAX(page_size, 1); + + limit = (double)MAX(content_size - page_size, 0); + value = MAX(0.0, adjustment->value); + value = MIN(value, limit); + + gtk_adjustment_configure(adjustment, value, 0, MAX(content_size, page_size), + page_size * 0.1, page_size * 0.9, page_size); +} + +static void +logical_scroll_pane_destroy(GtkObject *this_) +{ + LogicalScrollPane *this = LOGICAL_SCROLL_PANE(this_); + + if (this->child) gtk_widget_unparent(this->child); + if (this->hscrollbar) gtk_widget_unparent(this->hscrollbar); + if (this->vscrollbar) gtk_widget_unparent(this->vscrollbar); + if (this->hadjustment) g_object_unref(this->hadjustment); + if (this->vadjustment) g_object_unref(this->vadjustment); + + this->child = NULL; + this->hscrollbar = NULL; + this->vscrollbar = NULL; + this->hadjustment = NULL; + this->vadjustment = NULL; + + GTK_OBJECT_CLASS(logical_scroll_pane_parent_class)->destroy(this_); +} + +static void +logical_scroll_pane_size_request(GtkWidget *this_, GtkRequisition *requisition) +{ + LogicalScrollPane *this = LOGICAL_SCROLL_PANE(this_); + GtkRequisition hreq; + GtkRequisition vreq; + + requisition->width = hreq.width = vreq.width = 0; + requisition->height = hreq.height = vreq.height = 0; + + if (this->hscrollbar && this->hscrollbar_visible) gtk_widget_size_request(this->hscrollbar, &hreq); + if (this->vscrollbar && this->vscrollbar_visible) gtk_widget_size_request(this->vscrollbar, &vreq); + + if (this->vscrollbar_visible) requisition->width += vreq.width; + if (this->hscrollbar_visible) requisition->height += hreq.height; + + requisition->width = MAX(requisition->width, hreq.width); + requisition->height = MAX(requisition->height, vreq.height); +} + +static void +logical_scroll_pane_size_allocate(GtkWidget *this_, GtkAllocation *allocation) +{ + LogicalScrollPane *this = LOGICAL_SCROLL_PANE(this_); + + GtkRequisition hreq; + GtkRequisition vreq; + + GtkAllocation child_allocation; + GtkAllocation hscrollbar_allocation; + GtkAllocation vscrollbar_allocation; + + int content_width; + int content_height; + double x, y, max; + + this_->allocation = *allocation; + + x = this->hadjustment->value + 0.5 * this->hadjustment->page_size; + y = this->vadjustment->value + 0.5 * this->vadjustment->page_size; + + hreq.width = hreq.height = 0; + vreq.width = vreq.height = 0; + + if (this->hscrollbar && this->hscrollbar_visible) gtk_widget_size_request(this->hscrollbar, &hreq); + if (this->vscrollbar && this->vscrollbar_visible) gtk_widget_size_request(this->vscrollbar, &vreq); + + content_width = allocation->width; + if (this->vscrollbar_visible) + content_width -= vreq.width; + content_width = MAX(content_width, 1); + + content_height = allocation->height; + if (this->hscrollbar_visible) + content_height -= hreq.height; + content_height = MAX(content_height, 1); + + logical_scroll_pane_configure_adjustment(this->hadjustment, this->content_width, content_width); + logical_scroll_pane_configure_adjustment(this->vadjustment, this->content_height, content_height); + + max = this->hadjustment->upper - this->hadjustment->page_size; + x -= 0.5 * this->hadjustment->page_size; + x = MIN(MAX(this->hadjustment->lower, x), max); + gtk_adjustment_set_value(this->hadjustment, x); + + max = this->vadjustment->upper - this->vadjustment->page_size; + y -= 0.5 * this->vadjustment->page_size; + y = MIN(MAX(this->vadjustment->lower, y), max); + gtk_adjustment_set_value(this->vadjustment, y); + + if (this->child && GTK_WIDGET_VISIBLE(this->child)) { + child_allocation.x = allocation->x; + child_allocation.y = allocation->y; + child_allocation.width = content_width; + child_allocation.height = content_height; + gtk_widget_size_allocate(this->child, &child_allocation); + } + + if (this->hscrollbar && this->hscrollbar_visible) { + hscrollbar_allocation.x = allocation->x; + hscrollbar_allocation.y = allocation->y + content_height; + hscrollbar_allocation.width = content_width; + hscrollbar_allocation.height = hreq.height; + gtk_widget_size_allocate(this->hscrollbar, &hscrollbar_allocation); + } + + if (this->vscrollbar && this->vscrollbar_visible) { + vscrollbar_allocation.x = allocation->x + content_width; + vscrollbar_allocation.y = allocation->y; + vscrollbar_allocation.width = vreq.width; + vscrollbar_allocation.height = content_height; + gtk_widget_size_allocate(this->vscrollbar, &vscrollbar_allocation); + } +} + +static void +logical_scroll_pane_add(GtkContainer *this_, GtkWidget *widget) +{ + LogicalScrollPane *this = LOGICAL_SCROLL_PANE(this_); + if (this->child) { + g_warning("LogicalScrollPane already has a child"); + return; + } + logical_scroll_pane_set_child(this, widget); +} + +static void +logical_scroll_pane_remove(GtkContainer *this_, GtkWidget *widget) +{ + LogicalScrollPane *this = LOGICAL_SCROLL_PANE(this_); + + if (widget == this->child) { + this->child = NULL; + GTK_BIN(this)->child = NULL; + } else if (widget == this->hscrollbar) { + this->hscrollbar = NULL; + } else if (widget == this->vscrollbar) { + this->vscrollbar = NULL; + } else { + g_warning("Widget is not a child of LogicalScrollPane"); + return; + } + + gtk_widget_unparent(widget); + gtk_widget_queue_resize(GTK_WIDGET(this)); +} + +static void +logical_scroll_pane_forall(GtkContainer *this_, int include_internals, GtkCallback callback, void *callback_data) +{ + LogicalScrollPane *this = LOGICAL_SCROLL_PANE(this_); + + if (this->child) (*callback)(this->child, callback_data); + + if (!include_internals) + return; + + if (this->hscrollbar) (*callback)(this->hscrollbar, callback_data); + if (this->vscrollbar) (*callback)(this->vscrollbar, callback_data); +} + +static void +set_adjustment(LogicalScrollPane *this, GtkAdjustment *adjustment, GtkAdjustment **slot, GtkWidget *scrollbar) +{ + if (!adjustment) + adjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 1.0, 1.0, 0.0)); + + if (*slot != adjustment) { + g_object_ref_sink(adjustment); + + if (*slot) { + g_signal_handlers_disconnect_by_data(*slot, this); + g_object_unref(*slot); + } + + *slot = adjustment; + g_signal_connect(*slot, "value-changed", + G_CALLBACK(&logical_scroll_pane_queue_child_draw), this); + } + + if (scrollbar) + gtk_range_set_adjustment(GTK_RANGE(scrollbar), adjustment); +} + +static void +logical_scroll_pane_set_adjustment(LogicalScrollPane *this, int horizontal, GtkAdjustment *adjustment) +{ + if (horizontal) + set_adjustment(this, adjustment, &this->hadjustment, this->hscrollbar); + else + set_adjustment(this, adjustment, &this->vadjustment, this->vscrollbar); +} + +static void +logical_scroll_pane_class_init(LogicalScrollPaneClass *class) +{ + GtkObjectClass *object_class = GTK_OBJECT_CLASS(class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(class); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS(class); + + object_class->destroy = &logical_scroll_pane_destroy; + + widget_class->size_request = &logical_scroll_pane_size_request; + widget_class->size_allocate = &logical_scroll_pane_size_allocate; + + container_class->add = &logical_scroll_pane_add; + container_class->remove = &logical_scroll_pane_remove; + container_class->forall = &logical_scroll_pane_forall; +} + +static void +logical_scroll_pane_init(LogicalScrollPane *this) +{ + this->child = NULL; + this->content_width = 1; + this->content_height = 1; + this->hscrollbar_visible = 1; + this->vscrollbar_visible = 1; + + this->hadjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, /* value */ + 0.0, /* lower */ + 0.0, /* upper */ + 1.0, /* step increment */ + 1.0, /* page increment */ + 0.0)); /* page size */ + + this->vadjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 1.0, 1.0, 0.0)); + + g_object_ref_sink(this->hadjustment); + g_object_ref_sink(this->vadjustment); + g_signal_connect(this->hadjustment, "value-changed", + G_CALLBACK(&logical_scroll_pane_queue_child_draw), this); + g_signal_connect(this->vadjustment, "value-changed", + G_CALLBACK(&logical_scroll_pane_queue_child_draw), this); + + this->hscrollbar = gtk_hscrollbar_new(this->hadjustment); + this->vscrollbar = gtk_vscrollbar_new(this->vadjustment); + + gtk_widget_set_parent(this->hscrollbar, GTK_WIDGET(this)); + gtk_widget_set_parent(this->vscrollbar, GTK_WIDGET(this)); + + gtk_widget_show(this->hscrollbar); + gtk_widget_show(this->vscrollbar); +} + +GtkWidget * +logical_scroll_pane_new(void) +{ + return GTK_WIDGET(g_object_new(LOGICAL_TYPE_SCROLL_PANE, NULL)); +} + +void +logical_scroll_pane_set_child(LogicalScrollPane *this, GtkWidget *child) +{ + if (child == this->child) + return; + + if (this->child) { + gtk_widget_unparent(this->child); + this->child = NULL; + GTK_BIN(this)->child = NULL; + } + + if (child) { + this->child = child; + GTK_BIN(this)->child = child; + gtk_widget_set_parent(child, GTK_WIDGET(this)); + if (GTK_WIDGET_VISIBLE(this)) + gtk_widget_show(child); + } + + gtk_widget_queue_resize(GTK_WIDGET(this)); +} + +GtkWidget * +logical_scroll_pane_get_child(LogicalScrollPane *this) +{ + return this->child; +} + +void +logical_scroll_pane_set_content_size(LogicalScrollPane *this, int width, int height) +{ + this->content_width = MAX(1, width); + this->content_height = MAX(1, height); + logical_scroll_pane_configure_adjustment(this->hadjustment, this->content_width, (int)this->hadjustment->page_size); + logical_scroll_pane_configure_adjustment(this->vadjustment, this->content_height, (int)this->vadjustment->page_size); + gtk_widget_queue_resize(GTK_WIDGET(this)); + if (this->child) + gtk_widget_queue_draw(this->child); +} + +static void +set_scrollbar(LogicalScrollPane *this, GtkWidget *scrollbar, GtkWidget **slot, int *scrollbar_visible) +{ + if (scrollbar == *slot) + return; + + if (scrollbar) { + if (*slot) + gtk_widget_unparent(*slot); + *slot = scrollbar; + gtk_widget_set_parent(scrollbar, GTK_WIDGET(this)); + + if (*scrollbar_visible) + gtk_widget_show(scrollbar); + } else { + if (*slot) { + gtk_widget_unparent(*slot); + *slot = NULL; + } + } + + gtk_widget_queue_resize(GTK_WIDGET(this)); +} + +void +logical_scroll_pane_set_hscrollbar(LogicalScrollPane *this, GtkWidget *scrollbar) +{ + set_scrollbar(this, scrollbar, &this->hscrollbar, &this->hscrollbar_visible); +} + +void +logical_scroll_pane_set_vscrollbar(LogicalScrollPane *this, GtkWidget *scrollbar) +{ + set_scrollbar(this, scrollbar, &this->vscrollbar, &this->vscrollbar_visible); +} + +GtkWidget * +logical_scroll_pane_get_hscrollbar(LogicalScrollPane *this) +{ + return this->hscrollbar; +} + +GtkWidget * +logical_scroll_pane_get_vscrollbar(LogicalScrollPane *this) +{ + return this->vscrollbar; +} + +static void +set_scrollbar_visible(LogicalScrollPane *this, int visible, GtkWidget **scrollbar, int *scrollbar_visible) +{ + visible = visible != 0; + + if (*scrollbar_visible == visible) + return; + + *scrollbar_visible = visible; + + if (*scrollbar) + gtk_widget_set_visible(*scrollbar, visible); + + gtk_widget_queue_resize(GTK_WIDGET(this)); +} + +void +logical_scroll_pane_set_hscrollbar_visible(LogicalScrollPane *this, int visible) +{ + set_scrollbar_visible(this, visible, &this->hscrollbar, &this->hscrollbar_visible); +} + +void +logical_scroll_pane_set_vscrollbar_visible(LogicalScrollPane *this, int visible) +{ + set_scrollbar_visible(this, visible, &this->vscrollbar, &this->vscrollbar_visible); +} + +int +logical_scroll_pane_get_hscrollbar_visible(LogicalScrollPane *this) +{ + return this->hscrollbar_visible; +} + +int +logical_scroll_pane_get_vscrollbar_visible(LogicalScrollPane *this) +{ + return this->vscrollbar_visible; +} + +void +logical_scroll_pane_set_hadjustment(LogicalScrollPane *this, GtkAdjustment *adjustment) +{ + logical_scroll_pane_set_adjustment(this, 1, adjustment); +} + +void +logical_scroll_pane_set_vadjustment(LogicalScrollPane *this, GtkAdjustment *adjustment) +{ + logical_scroll_pane_set_adjustment(this, 0, adjustment); +} + +GtkAdjustment * +logical_scroll_pane_get_hadjustment(LogicalScrollPane *this) +{ + return this->hadjustment; +} + +GtkAdjustment * +logical_scroll_pane_get_vadjustment(LogicalScrollPane *this) +{ + return this->vadjustment; +} |
