251 lines
7.4 KiB
C
251 lines
7.4 KiB
C
#include "aspect_container.h"
|
|
|
|
struct _AspectContainer {
|
|
GtkWidget parent_instance;
|
|
float aspect_ratio;
|
|
int max_width;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_ASPECT_RATIO,
|
|
PROP_MAX_WIDTH,
|
|
LAST_PROP
|
|
};
|
|
|
|
static GParamSpec *props[LAST_PROP];
|
|
|
|
G_DEFINE_TYPE(AspectContainer, aspect_container, GTK_TYPE_WIDGET)
|
|
|
|
static void
|
|
aspect_container_set_property(GObject *object, guint prop_id,
|
|
const GValue *value, GParamSpec *pspec)
|
|
{
|
|
AspectContainer *self = ASPECT_CONTAINER(object);
|
|
switch (prop_id) {
|
|
case PROP_ASPECT_RATIO:
|
|
aspect_container_set_aspect_ratio(self, g_value_get_float(value));
|
|
break;
|
|
case PROP_MAX_WIDTH:
|
|
aspect_container_set_max_width(self, g_value_get_int(value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
aspect_container_get_property(GObject *object, guint prop_id,
|
|
GValue *value, GParamSpec *pspec)
|
|
{
|
|
AspectContainer *self = ASPECT_CONTAINER(object);
|
|
switch (prop_id) {
|
|
case PROP_ASPECT_RATIO:
|
|
g_value_set_float(value, self->aspect_ratio);
|
|
break;
|
|
case PROP_MAX_WIDTH:
|
|
g_value_set_int(value, self->max_width);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
aspect_container_dispose(GObject *object)
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET(object);
|
|
GtkWidget *child;
|
|
|
|
while ((child = gtk_widget_get_first_child(widget)))
|
|
gtk_widget_unparent(child);
|
|
|
|
G_OBJECT_CLASS(aspect_container_parent_class)->dispose(object);
|
|
}
|
|
|
|
static GtkSizeRequestMode
|
|
aspect_container_get_request_mode(GtkWidget *widget)
|
|
{
|
|
(void)widget;
|
|
return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
|
|
}
|
|
|
|
static void
|
|
aspect_container_size_allocate(GtkWidget *widget,
|
|
int width, int height, int baseline)
|
|
{
|
|
GtkWidget *child = gtk_widget_get_first_child(widget);
|
|
if (!child)
|
|
return;
|
|
|
|
GtkAllocation alloc = {
|
|
.x = 0,
|
|
.y = 0,
|
|
.width = width,
|
|
.height = height,
|
|
};
|
|
gtk_widget_size_allocate(child, &alloc, baseline);
|
|
}
|
|
|
|
static void
|
|
aspect_container_measure(GtkWidget *widget,
|
|
GtkOrientation orientation,
|
|
int for_size,
|
|
int *minimum,
|
|
int *natural,
|
|
int *minimum_baseline,
|
|
int *natural_baseline)
|
|
{
|
|
AspectContainer *self = ASPECT_CONTAINER(widget);
|
|
|
|
if (minimum_baseline)
|
|
*minimum_baseline = -1;
|
|
if (natural_baseline)
|
|
*natural_baseline = -1;
|
|
|
|
GtkWidget *child = gtk_widget_get_first_child(widget);
|
|
gboolean has_child = child && gtk_widget_get_visible(child);
|
|
|
|
if (orientation == GTK_ORIENTATION_HORIZONTAL) {
|
|
if (has_child)
|
|
gtk_widget_measure(child, GTK_ORIENTATION_HORIZONTAL, -1,
|
|
NULL, NULL, NULL, NULL);
|
|
|
|
int w = self->max_width > 0 ? self->max_width : 0;
|
|
if (minimum) *minimum = w;
|
|
if (natural) *natural = w;
|
|
} else {
|
|
if (for_size >= 0) {
|
|
int w = self->max_width > 0 && for_size > self->max_width
|
|
? self->max_width : for_size;
|
|
int h_min = (int)(w * self->aspect_ratio);
|
|
int h_nat = (int)(w * self->aspect_ratio);
|
|
if (minimum) *minimum = h_min;
|
|
if (natural) *natural = h_nat;
|
|
} else if (has_child) {
|
|
int child_min_w = 0, child_nat_w = 0;
|
|
gtk_widget_measure(child, GTK_ORIENTATION_HORIZONTAL, -1,
|
|
&child_min_w, &child_nat_w, NULL, NULL);
|
|
|
|
int min_w = child_min_w;
|
|
int nat_w = child_nat_w >= child_min_w ? child_nat_w : child_min_w;
|
|
|
|
int req_w = 0, req_h = 0;
|
|
gtk_widget_get_size_request(widget, &req_w, &req_h);
|
|
if (req_w > min_w) min_w = req_w;
|
|
if (req_w > nat_w) nat_w = req_w;
|
|
|
|
if (self->max_width > 0) {
|
|
if (min_w > self->max_width) min_w = self->max_width;
|
|
if (nat_w > self->max_width) nat_w = self->max_width;
|
|
}
|
|
|
|
int h_min = (int)(min_w * self->aspect_ratio);
|
|
int h_nat = (int)(nat_w * self->aspect_ratio);
|
|
if (minimum) *minimum = h_min;
|
|
if (natural) *natural = h_nat;
|
|
} else {
|
|
if (minimum) *minimum = 0;
|
|
if (natural) *natural = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
aspect_container_compute_expand(GtkWidget *widget,
|
|
gboolean *hexpand_p,
|
|
gboolean *vexpand_p)
|
|
{
|
|
GtkWidget *child = gtk_widget_get_first_child(widget);
|
|
if (child) {
|
|
*hexpand_p = gtk_widget_compute_expand(child, GTK_ORIENTATION_HORIZONTAL);
|
|
*vexpand_p = gtk_widget_compute_expand(child, GTK_ORIENTATION_VERTICAL);
|
|
} else {
|
|
*hexpand_p = FALSE;
|
|
*vexpand_p = FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
aspect_container_class_init(AspectContainerClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
|
|
|
|
gobject_class->set_property = aspect_container_set_property;
|
|
gobject_class->get_property = aspect_container_get_property;
|
|
gobject_class->dispose = aspect_container_dispose;
|
|
|
|
widget_class->get_request_mode = aspect_container_get_request_mode;
|
|
widget_class->size_allocate = aspect_container_size_allocate;
|
|
widget_class->measure = aspect_container_measure;
|
|
widget_class->compute_expand = aspect_container_compute_expand;
|
|
|
|
props[PROP_ASPECT_RATIO] = g_param_spec_float(
|
|
"aspect-ratio", NULL, NULL,
|
|
0.0, G_MAXFLOAT, 1.0,
|
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
props[PROP_MAX_WIDTH] = g_param_spec_int(
|
|
"max-width", NULL, NULL,
|
|
0, G_MAXINT, 0,
|
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
g_object_class_install_properties(gobject_class, LAST_PROP, props);
|
|
|
|
gtk_widget_class_set_css_name(widget_class, "aspectcontainer");
|
|
gtk_widget_class_set_accessible_role(widget_class, GTK_ACCESSIBLE_ROLE_GENERIC);
|
|
}
|
|
|
|
static void
|
|
aspect_container_init(AspectContainer *self)
|
|
{
|
|
self->aspect_ratio = 1.0;
|
|
self->max_width = 0;
|
|
}
|
|
|
|
GtkWidget *
|
|
aspect_container_new(float aspect_ratio)
|
|
{
|
|
return g_object_new(ASPECT_TYPE_CONTAINER,
|
|
"aspect-ratio", aspect_ratio,
|
|
NULL);
|
|
}
|
|
|
|
void
|
|
aspect_container_set_aspect_ratio(AspectContainer *self, float ratio)
|
|
{
|
|
g_return_if_fail(ASPECT_IS_CONTAINER(self));
|
|
if (self->aspect_ratio == ratio)
|
|
return;
|
|
self->aspect_ratio = ratio;
|
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ASPECT_RATIO]);
|
|
gtk_widget_queue_resize(GTK_WIDGET(self));
|
|
}
|
|
|
|
float
|
|
aspect_container_get_aspect_ratio(AspectContainer *self)
|
|
{
|
|
g_return_val_if_fail(ASPECT_IS_CONTAINER(self), 1.0);
|
|
return self->aspect_ratio;
|
|
}
|
|
|
|
void
|
|
aspect_container_set_max_width(AspectContainer *self, int max_width)
|
|
{
|
|
g_return_if_fail(ASPECT_IS_CONTAINER(self));
|
|
if (self->max_width == max_width)
|
|
return;
|
|
self->max_width = max_width;
|
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_MAX_WIDTH]);
|
|
gtk_widget_queue_resize(GTK_WIDGET(self));
|
|
}
|
|
|
|
int
|
|
aspect_container_get_max_width(AspectContainer *self)
|
|
{
|
|
g_return_val_if_fail(ASPECT_IS_CONTAINER(self), 0);
|
|
return self->max_width;
|
|
}
|