Replace use of GtkAlignment with a custom align widget

To use the GtkAlignment we have to play evil tricks overriding
its size request, to make it reallocate the child to the preferred
size we desire based on the virtual desktop size + zoom level.

By replacing the GtkAlignment with a custom widget we can
directly implement the layout/sizing semantics we want without
playing stupid games
This commit is contained in:
Daniel P. Berrange 2011-07-01 17:25:01 +01:00
parent f3fa999769
commit cff795065a
6 changed files with 452 additions and 92 deletions

View File

@ -16,6 +16,7 @@ virt_viewer_SOURCES = \
virt-viewer-events.h virt-viewer-events.c \
virt-viewer.h virt-viewer.c \
virt-viewer-priv.h \
virt-viewer-align.h virt-viewer-align.c \
virt-viewer-display.h virt-viewer-display.c \
virt-viewer-display-vnc.h virt-viewer-display-vnc.c \
view/autoDrawer.c \

342
src/virt-viewer-align.c Normal file
View File

@ -0,0 +1,342 @@
/*
* Virt Viewer: A virtual machine console viewer
*
* Copyright (C) 2007-2011 Red Hat,
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#include <config.h>
#include <locale.h>
#include "virt-viewer-align.h"
#define VIRT_VIEWER_ALIGN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), VIRT_VIEWER_TYPE_ALIGN, VirtViewerAlignPrivate))
struct _VirtViewerAlignPrivate
{
gboolean dirty;
guint preferred_width;
guint preferred_height;
guint zoom_level;
gboolean zoom;
};
static void virt_viewer_align_size_request(GtkWidget *widget,
GtkRequisition *requisition);
static void virt_viewer_align_size_allocate(GtkWidget *widget,
GtkAllocation *allocation);
static void virt_viewer_align_set_property(GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void virt_viewer_align_get_property(GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
G_DEFINE_TYPE(VirtViewerAlign, virt_viewer_align, GTK_TYPE_BIN)
enum {
PROP_0,
PROP_PREFERRED_WIDTH,
PROP_PREFERRED_HEIGHT,
PROP_ZOOM,
PROP_ZOOM_LEVEL,
};
static void
virt_viewer_align_class_init(VirtViewerAlignClass *class)
{
GObjectClass *gobject_class;
GtkWidgetClass *widget_class;
gobject_class = (GObjectClass*) class;
widget_class = (GtkWidgetClass*) class;
gobject_class->set_property = virt_viewer_align_set_property;
gobject_class->get_property = virt_viewer_align_get_property;
widget_class->size_request = virt_viewer_align_size_request;
widget_class->size_allocate = virt_viewer_align_size_allocate;
g_object_class_install_property(gobject_class,
PROP_PREFERRED_WIDTH,
g_param_spec_int("preferred-width",
"Width",
"Preferred width",
100,
G_MAXINT32,
100,
G_PARAM_READWRITE));
g_object_class_install_property(gobject_class,
PROP_PREFERRED_HEIGHT,
g_param_spec_int("preferred-height",
"Height",
"Preferred height",
100,
G_MAXINT32,
100,
G_PARAM_READWRITE));
g_object_class_install_property(gobject_class,
PROP_ZOOM,
g_param_spec_boolean("zoom",
"Zoom",
"Zoom",
TRUE,
G_PARAM_READWRITE));
g_object_class_install_property(gobject_class,
PROP_ZOOM_LEVEL,
g_param_spec_int("zoom-level",
"Zoom",
"Zoom level",
10,
400,
100,
G_PARAM_READWRITE));
g_type_class_add_private(gobject_class, sizeof(VirtViewerAlignPrivate));
}
static void
virt_viewer_align_init(VirtViewerAlign *align)
{
gtk_widget_set_has_window(GTK_WIDGET(align), FALSE);
gtk_widget_set_redraw_on_allocate(GTK_WIDGET(align), FALSE);
align->priv = VIRT_VIEWER_ALIGN_GET_PRIVATE(align);
align->priv->preferred_width = 100;
align->priv->preferred_height = 100;
align->priv->zoom_level = 100;
align->priv->zoom = TRUE;
}
GtkWidget*
virt_viewer_align_new(void)
{
return g_object_new (VIRT_VIEWER_TYPE_ALIGN, NULL);
}
static void
virt_viewer_align_set_property(GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
VirtViewerAlign *align = VIRT_VIEWER_ALIGN(object);
VirtViewerAlignPrivate *priv = align->priv;
switch (prop_id) {
case PROP_PREFERRED_WIDTH:
virt_viewer_align_set_preferred_size(align,
g_value_get_int(value),
priv->preferred_height);
break;
case PROP_PREFERRED_HEIGHT:
virt_viewer_align_set_preferred_size(align,
priv->preferred_width,
g_value_get_int(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
virt_viewer_align_get_property(GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
VirtViewerAlign *align = VIRT_VIEWER_ALIGN(object);
VirtViewerAlignPrivate *priv = align->priv;
switch (prop_id) {
case PROP_PREFERRED_WIDTH:
g_value_set_int(value, priv->preferred_width);
break;
case PROP_PREFERRED_HEIGHT:
g_value_set_int(value, priv->preferred_height);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
virt_viewer_align_idle(gpointer opaque)
{
VirtViewerAlign *align = opaque;
VirtViewerAlignPrivate *priv = align->priv;
if (!priv->dirty)
gtk_widget_queue_resize_no_redraw(GTK_WIDGET(align));
return FALSE;
}
static void
virt_viewer_align_size_request(GtkWidget *widget,
GtkRequisition *requisition)
{
VirtViewerAlign *align = VIRT_VIEWER_ALIGN(widget);
VirtViewerAlignPrivate *priv = align->priv;
requisition->width = GTK_CONTAINER(widget)->border_width * 2;
requisition->height = GTK_CONTAINER(widget)->border_width * 2;
if (priv->dirty) {
if (priv->zoom)
requisition->width += priv->preferred_width * priv->zoom_level / 100;
else
requisition->width += priv->preferred_width;
} else {
requisition->width += 50;
}
if (priv->dirty) {
if (priv->zoom)
requisition->height += priv->preferred_height * priv->zoom_level / 100;
else
requisition->height += priv->preferred_height;
} else {
requisition->height += 50;
}
if (priv->dirty) {
g_idle_add(virt_viewer_align_idle, widget);
priv->dirty = FALSE;
}
}
static void
virt_viewer_align_size_allocate(GtkWidget *widget,
GtkAllocation *allocation)
{
GtkBin *bin = GTK_BIN(widget);
VirtViewerAlign *align = VIRT_VIEWER_ALIGN(widget);
VirtViewerAlignPrivate *priv = align->priv;
GtkAllocation child_allocation;
gint width, height;
gint border_width;
double preferredAspect;
double actualAspect;
widget->allocation = *allocation;
preferredAspect = (double)priv->preferred_width / (double)priv->preferred_height;
if (bin->child && gtk_widget_get_visible(bin->child)) {
border_width = GTK_CONTAINER(align)->border_width;
width = MAX(1, allocation->width - 2 * border_width);
height = MAX(1, allocation->height - 2 * border_width);
actualAspect = (double)width / (double)height;
if (actualAspect > preferredAspect) {
child_allocation.width = height * preferredAspect;
child_allocation.height = height;
} else {
child_allocation.width = width;
child_allocation.height = width / preferredAspect;
}
child_allocation.x = 0.5 * (width - child_allocation.width) + allocation->x + border_width;
child_allocation.y = 0.5 * (height - child_allocation.height) + allocation->y + border_width;
gtk_widget_size_allocate(bin->child, &child_allocation);
}
}
void virt_viewer_align_set_preferred_size(VirtViewerAlign *align,
guint width,
guint height)
{
VirtViewerAlignPrivate *priv = align->priv;
priv->preferred_width = width;
priv->preferred_height = height;
priv->dirty = TRUE;
gtk_widget_queue_resize(GTK_WIDGET(align));
}
void virt_viewer_align_set_zoom_level(VirtViewerAlign *align,
guint zoom)
{
VirtViewerAlignPrivate *priv = align->priv;
GtkBin *bin = GTK_BIN(align);
if (zoom < 10)
zoom = 10;
if (zoom > 400)
zoom = 400;
priv->zoom_level = zoom;
if (bin->child && gtk_widget_get_visible(bin->child)) {
priv->dirty = TRUE;
gtk_widget_queue_resize(GTK_WIDGET(align));
}
}
void virt_viewer_align_zoom_in(VirtViewerAlign *align)
{
VirtViewerAlignPrivate *priv = align->priv;
virt_viewer_align_set_zoom_level(align, priv->zoom_level + 10);
}
void virt_viewer_align_zoom_out(VirtViewerAlign *align)
{
VirtViewerAlignPrivate *priv = align->priv;
virt_viewer_align_set_zoom_level(align, priv->zoom_level - 10);
}
void virt_viewer_align_zoom_normal(VirtViewerAlign *align)
{
virt_viewer_align_set_zoom_level(align, 100);
}
void virt_viewer_align_set_zoom(VirtViewerAlign *align,
gboolean zoom)
{
VirtViewerAlignPrivate *priv = align->priv;
GtkBin *bin = GTK_BIN(align);
priv->zoom = zoom;
if (bin->child && gtk_widget_get_visible(bin->child)) {
priv->dirty = TRUE;
gtk_widget_queue_resize(GTK_WIDGET(align));
}
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/

86
src/virt-viewer-align.h Normal file
View File

@ -0,0 +1,86 @@
/*
* Virt Viewer: A virtual machine console viewer
*
* Copyright (C) 2007-2011 Red Hat,
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Daniel P. Berrange <berrange@redhat.com>
*/
#ifndef _VIRT_VIEWER_ALIGN_H
#define _VIRT_VIEWER_ALIGN_H
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define VIRT_VIEWER_TYPE_ALIGN virt_viewer_align_get_type()
#define VIRT_VIEWER_ALIGN(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), VIRT_VIEWER_TYPE_ALIGN, VirtViewerAlign))
#define VIRT_VIEWER_ALIGN_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), VIRT_VIEWER_TYPE_ALIGN, VirtViewerAlignClass))
#define VIRT_VIEWER_IS_ALIGN(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIRT_VIEWER_TYPE_ALIGN))
#define VIRT_VIEWER_IS_ALIGN_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), VIRT_VIEWER_TYPE_ALIGN))
#define VIRT_VIEWER_ALIGN_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), VIRT_VIEWER_TYPE_ALIGN, VirtViewerAlignClass))
typedef struct _VirtViewerAlign VirtViewerAlign;
typedef struct _VirtViewerAlignClass VirtViewerAlignClass;
typedef struct _VirtViewerAlignPrivate VirtViewerAlignPrivate;
struct _VirtViewerAlign {
GtkBin parent;
VirtViewerAlignPrivate *priv;
};
struct _VirtViewerAlignClass {
GtkBinClass parent_class;
};
GType virt_viewer_align_get_type(void);
GtkWidget *virt_viewer_align_new(void);
void virt_viewer_align_set_preferred_size(VirtViewerAlign *align,
guint width,
guint height);
void virt_viewer_align_zoom_in(VirtViewerAlign *align);
void virt_viewer_align_zoom_out(VirtViewerAlign *align);
void virt_viewer_align_zoom_normal(VirtViewerAlign *align);
void virt_viewer_align_set_zoom_level(VirtViewerAlign *align,
guint zoom);
void virt_viewer_align_set_zoom(VirtViewerAlign *align,
gboolean zoom);
G_END_DECLS
#endif /* _VIRT_VIEWER_ALIGN_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/

View File

@ -65,8 +65,6 @@ struct _VirtViewer {
char *pretty_address;
int zoomlevel;
int desktopWidth;
int desktopHeight;
gboolean autoResize;

View File

@ -47,6 +47,7 @@
#include "virt-viewer.h"
#include "virt-viewer-priv.h"
#include "virt-viewer-align.h"
#include "virt-viewer-events.h"
#include "virt-viewer-auth.h"
#include "virt-viewer-display-vnc.h"
@ -57,8 +58,6 @@
#include "view/autoDrawer.h"
#define SCALE(x) do { x = viewer->fullscreen ? x : x * viewer->zoomlevel / 100; } while (0);
gboolean doDebug = FALSE;
/* Signal handlers for main window */
@ -161,69 +160,6 @@ virt_viewer_add_display_and_realize(VirtViewer *viewer)
gtk_widget_realize(viewer->display->widget);
}
/* Now that the size is set to our preferred sizing, this
* triggers another resize calculation but without our
* scrolled window callback active. This is the key that
* allows us to set the fixed size, but then allow the user
* to later resize it smaller again
*/
static gboolean
virt_viewer_unset_widget_size_cb(gpointer data)
{
GtkWidget *widget = data;
DEBUG_LOG("Unset requisition on widget=%p", widget);
gtk_widget_queue_resize_no_redraw (widget);
return FALSE;
}
/*
* This sets the actual size of the widget, and then
* sets an idle callback to resize again, without constraints
* activated
*/
static gboolean
virt_viewer_set_widget_size_cb(GtkWidget *widget,
GtkRequisition *req,
gpointer data)
{
VirtViewerSize *size = data;
DEBUG_LOG("Set requisition on widget=%p to %dx%d", widget, size->width, size->height);
req->width = size->width;
req->height = size->height;
g_signal_handler_disconnect(widget, size->sig_id);
g_free(size);
g_idle_add(virt_viewer_unset_widget_size_cb, widget);
return FALSE;
}
/*
* Registers a callback used to set the widget size once
*/
static void
virt_viewer_set_widget_size(VirtViewer *viewer,
GtkWidget *widget,
int width,
int height)
{
VirtViewerSize *size = g_new (VirtViewerSize, 1);
DEBUG_LOG("Queue resize widget=%p width=%d height=%d", widget, width, height);
size->viewer = viewer;
size->width = width;
size->height = height;
size->sig_id = g_signal_connect
(widget, "size-request",
G_CALLBACK (virt_viewer_set_widget_size_cb),
size);
gtk_widget_queue_resize (widget);
}
/*
* This code attempts to resize the top level window to be large enough
@ -273,44 +209,32 @@ virt_viewer_resize_main_window(VirtViewer *viewer)
height = viewer->desktopHeight;
}
SCALE(width);
SCALE(height);
virt_viewer_set_widget_size(viewer,
viewer->align,
width,
height);
virt_viewer_align_set_preferred_size(VIRT_VIEWER_ALIGN(viewer->align),
width, height);
}
void
virt_viewer_menu_view_zoom_out(GtkWidget *menu G_GNUC_UNUSED,
VirtViewer *viewer)
{
viewer->zoomlevel -= 10;
if (viewer->zoomlevel < 10)
viewer->zoomlevel = 10;
virt_viewer_resize_main_window(viewer);
gtk_window_resize(GTK_WINDOW(viewer->window), 1, 1);
virt_viewer_align_zoom_out(VIRT_VIEWER_ALIGN(viewer->align));
}
void
virt_viewer_menu_view_zoom_in(GtkWidget *menu G_GNUC_UNUSED,
VirtViewer *viewer)
{
viewer->zoomlevel += 10;
if (viewer->zoomlevel > 200)
viewer->zoomlevel = 200;
virt_viewer_resize_main_window(viewer);
gtk_window_resize(GTK_WINDOW(viewer->window), 1, 1);
virt_viewer_align_zoom_in(VIRT_VIEWER_ALIGN(viewer->align));
}
void
virt_viewer_menu_view_zoom_reset(GtkWidget *menu G_GNUC_UNUSED,
VirtViewer *viewer)
{
viewer->zoomlevel = 100;
virt_viewer_resize_main_window(viewer);
gtk_window_resize(GTK_WINDOW(viewer->window), 1, 1);
virt_viewer_align_zoom_normal(VIRT_VIEWER_ALIGN(viewer->align));
}
void
@ -1397,7 +1321,6 @@ virt_viewer_start(const char *uri,
viewer->verbose = verbose;
viewer->domkey = g_strdup(name);
viewer->uri = g_strdup(uri);
viewer->zoomlevel = zoom;
g_value_init(&viewer->accelSetting, G_TYPE_STRING);
@ -1427,7 +1350,9 @@ virt_viewer_start(const char *uri,
}
viewer->status = gtk_label_new("");
viewer->align = gtk_alignment_new(0.5, 0.5, 0, 0);
viewer->align = virt_viewer_align_new();
virt_viewer_align_set_zoom_level(VIRT_VIEWER_ALIGN(viewer->align), zoom);
viewer->notebook = gtk_notebook_new();
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(viewer->notebook), FALSE);

View File

@ -41,6 +41,7 @@
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<signal name="activate" handler="virt_viewer_menu_file_quit"/>
<accelerator key="q" signal="activate" modifiers="GDK_CONTROL_MASK|GDK_SHIFT_MASK"/>
</object>
</child>
</object>
@ -60,6 +61,7 @@
<property name="visible">True</property>
<property name="label" translatable="yes">Full screen</property>
<property name="use_underline">True</property>
<accelerator key="F11" signal="activate"/>
<signal name="toggled" handler="virt_viewer_menu_view_fullscreen"/>
</object>
</child>
@ -72,17 +74,21 @@
<object class="GtkMenu" id="menu4">
<property name="visible">True</property>
<child>
<object class="GtkMenuItem" id="menu-view-zoom-in">
<object class="GtkImageMenuItem" id="menu-view-zoom-in">
<property name="visible">True</property>
<property name="label">gtk-zoom-in</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<accelerator key="plus" signal="activate" modifiers="GDK_CONTROL_MASK"/>
<signal name="activate" handler="virt_viewer_menu_view_zoom_in"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-view-zoom-out">
<object class="GtkImageMenuItem" id="menu-view-zoom-out">
<property name="visible">True</property>
<property name="label">gtk-zoom-out</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<accelerator key="minus" signal="activate" modifiers="GDK_CONTROL_MASK"/>
<signal name="activate" handler="virt_viewer_menu_view_zoom_out"/>
</object>
@ -93,9 +99,11 @@
</object>
</child>
<child>
<object class="GtkMenuItem" id="menu-view-zoom-reset">
<object class="GtkImageMenuItem" id="menu-view-zoom-reset">
<property name="visible">True</property>
<property name="label">gtk-zoom-100</property>
<property name="use_underline">True</property>
<property name="use_stock">True</property>
<accelerator key="0" signal="activate" modifiers="GDK_CONTROL_MASK"/>
<signal name="activate" handler="virt_viewer_menu_view_zoom_reset"/>
</object>