diff options
| author | Přemysl Eric Janouch <p@janouch.name> | 2021-11-29 00:35:39 +0100 | 
|---|---|---|
| committer | Přemysl Eric Janouch <p@janouch.name> | 2021-12-17 07:01:37 +0100 | 
| commit | 1c2a441cb56860d6bf558270b4a77d64dac2b954 (patch) | |
| tree | ec0f98588ff114476ede93143e4f7e45cb59f056 | |
| parent | 0b6b3d8290d6998f0190a6e78386dcf58ab8b4dd (diff) | |
| download | fiv-1c2a441cb56860d6bf558270b4a77d64dac2b954.tar.gz fiv-1c2a441cb56860d6bf558270b4a77d64dac2b954.tar.xz fiv-1c2a441cb56860d6bf558270b4a77d64dac2b954.zip  | |
Add a simple toolbar to the view
There is still considerable work to be done.
| -rw-r--r-- | fastiv-view.c | 142 | ||||
| -rw-r--r-- | fastiv-view.h | 26 | ||||
| -rw-r--r-- | fastiv.c | 212 | ||||
| -rw-r--r-- | resources/blend-tool-symbolic.svg | 154 | ||||
| -rw-r--r-- | resources/resources.gresource.xml | 1 | 
5 files changed, 477 insertions, 58 deletions
diff --git a/fastiv-view.c b/fastiv-view.c index 81e4add..985f52c 100644 --- a/fastiv-view.c +++ b/fastiv-view.c @@ -530,7 +530,6 @@ start_animating(FastivView *self)  static void  switch_page(FastivView *self, cairo_surface_t *page)  { -	GtkWidget *widget = GTK_WIDGET(self);  	self->frame = self->page = page;  	if ((self->orientation = (uintptr_t) cairo_surface_get_user_data(  			 self->page, &fastiv_io_key_orientation)) == @@ -538,7 +537,7 @@ switch_page(FastivView *self, cairo_surface_t *page)  		self->orientation = FastivIoOrientation0;  	start_animating(self); -	gtk_widget_queue_resize(widget); +	gtk_widget_queue_resize(GTK_WIDGET(self));  }  static void @@ -697,6 +696,13 @@ save_as(FastivView *self, gboolean frame)  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +static inline gboolean +command(FastivView *self, FastivViewCommand command) +{ +	fastiv_view_command(self, command); +	return TRUE; +} +  static gboolean  fastiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event)  { @@ -712,15 +718,15 @@ fastiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event)  	if (state == GDK_CONTROL_MASK) {  		switch (event->keyval) {  		case GDK_KEY_0: -			return set_scale(self, 1.0); +			return command(self, FASTIV_VIEW_COMMAND_ZOOM_1);  		case GDK_KEY_plus: -			return set_scale(self, self->scale * SCALE_STEP); +			return command(self, FASTIV_VIEW_COMMAND_ZOOM_IN);  		case GDK_KEY_minus: -			return set_scale(self, self->scale / SCALE_STEP); +			return command(self, FASTIV_VIEW_COMMAND_ZOOM_OUT);  		case GDK_KEY_p: -			return print(self); +			return command(self, FASTIV_VIEW_COMMAND_PRINT);  		case GDK_KEY_s: -			return save_as(self, FALSE); +			return command(self, FASTIV_VIEW_COMMAND_SAVE_PAGE);  		case GDK_KEY_S:  			return save_as(self, TRUE);  		} @@ -740,9 +746,9 @@ fastiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event)  	case GDK_KEY_9:  		return set_scale(self, event->keyval - GDK_KEY_0);  	case GDK_KEY_plus: -		return set_scale(self, self->scale * SCALE_STEP); +		return command(self, FASTIV_VIEW_COMMAND_ZOOM_IN);  	case GDK_KEY_minus: -		return set_scale(self, self->scale / SCALE_STEP); +		return command(self, FASTIV_VIEW_COMMAND_ZOOM_OUT);  	case GDK_KEY_x:  // Inspired by gThumb.  		return set_scale_to_fit(self, !self->scale_to_fit); @@ -753,47 +759,21 @@ fastiv_view_key_press_event(GtkWidget *widget, GdkEventKey *event)  		return TRUE;  	case GDK_KEY_less: -		self->orientation = view_left[self->orientation]; -		gtk_widget_queue_resize(widget); -		return TRUE; +		return command(self, FASTIV_VIEW_COMMAND_ROTATE_LEFT);  	case GDK_KEY_equal: -		self->orientation = view_mirror[self->orientation]; -		gtk_widget_queue_draw(widget); -		return TRUE; +		return command(self, FASTIV_VIEW_COMMAND_MIRROR);  	case GDK_KEY_greater: -		self->orientation = view_right[self->orientation]; -		gtk_widget_queue_resize(widget); -		return TRUE; +		return command(self, FASTIV_VIEW_COMMAND_ROTATE_RIGHT); -	case GDK_KEY_bracketleft: { -		cairo_surface_t *page = cairo_surface_get_user_data( -			self->page, &fastiv_io_key_page_previous); -		if (page) -			switch_page(self, page); -		return TRUE; -	} -	case GDK_KEY_bracketright: { -		cairo_surface_t *page = cairo_surface_get_user_data( -			self->page, &fastiv_io_key_page_next); -		if (page) -			switch_page(self, page); -		return TRUE; -	} +	case GDK_KEY_bracketleft: +		return command(self, FASTIV_VIEW_COMMAND_PAGE_PREVIOUS); +	case GDK_KEY_bracketright: +		return command(self, FASTIV_VIEW_COMMAND_PAGE_NEXT);  	case GDK_KEY_braceleft: -		stop_animating(self); -		if (!(self->frame = cairo_surface_get_user_data( -				self->frame, &fastiv_io_key_frame_previous))) -			self->frame = self->page; -		gtk_widget_queue_draw(widget); -		return TRUE; +		return command(self, FASTIV_VIEW_COMMAND_FRAME_PREVIOUS);  	case GDK_KEY_braceright: -		stop_animating(self); -		if (!(self->frame = cairo_surface_get_user_data( -				self->frame, &fastiv_io_key_frame_next))) -			self->frame = self->page; -		gtk_widget_queue_draw(widget); -		return TRUE; +		return command(self, FASTIV_VIEW_COMMAND_FRAME_NEXT);  	}  	return FALSE;  } @@ -858,3 +838,77 @@ fastiv_view_open(FastivView *self, const gchar *path, GError **error)  	set_scale_to_fit(self, true);  	return TRUE;  } + +static void +page_step(FastivView *self, int step) +{ +	cairo_user_data_key_t *key = +		step < 0 ? &fastiv_io_key_page_previous : &fastiv_io_key_page_next; +	cairo_surface_t *page = cairo_surface_get_user_data(self->page, key); +	if (page) +		switch_page(self, page); +} + +static void +frame_step(FastivView *self, int step) +{ +	stop_animating(self); +	cairo_user_data_key_t *key = +		step < 0 ? &fastiv_io_key_frame_previous : &fastiv_io_key_frame_next; +	if (!step || !(self->frame = cairo_surface_get_user_data(self->frame, key))) +		self->frame = self->page; +	gtk_widget_queue_draw(GTK_WIDGET(self)); +} + +void +fastiv_view_command(FastivView *self, FastivViewCommand command) +{ +	g_return_if_fail(FASTIV_IS_VIEW(self)); + +	GtkWidget *widget = GTK_WIDGET(self); +	if (!self->image) +		return; + +	switch (command) { +	break; case FASTIV_VIEW_COMMAND_ROTATE_LEFT: +		self->orientation = view_left[self->orientation]; +		gtk_widget_queue_resize(widget); +	break; case FASTIV_VIEW_COMMAND_MIRROR: +		self->orientation = view_mirror[self->orientation]; +		gtk_widget_queue_resize(widget); +	break; case FASTIV_VIEW_COMMAND_ROTATE_RIGHT: +		self->orientation = view_right[self->orientation]; +		gtk_widget_queue_resize(widget); + +	break; case FASTIV_VIEW_COMMAND_PAGE_FIRST: +		switch_page(self, self->image); +	break; case FASTIV_VIEW_COMMAND_PAGE_PREVIOUS: +		page_step(self, -1); +	break; case FASTIV_VIEW_COMMAND_PAGE_NEXT: +		page_step(self, +1); +	break; case FASTIV_VIEW_COMMAND_PAGE_LAST: +		for (cairo_surface_t *s = self->page; +			 (s = cairo_surface_get_user_data(s, &fastiv_io_key_page_next)); ) +			self->page = s; +		switch_page(self, self->page); + +	break; case FASTIV_VIEW_COMMAND_FRAME_FIRST: +		frame_step(self, 0); +	break; case FASTIV_VIEW_COMMAND_FRAME_PREVIOUS: +		frame_step(self, -1); +	break; case FASTIV_VIEW_COMMAND_FRAME_NEXT: +		frame_step(self, +1); + +	break; case FASTIV_VIEW_COMMAND_PRINT: +		print(self); +	break; case FASTIV_VIEW_COMMAND_SAVE_PAGE: +		save_as(self, FALSE); + +	break; case FASTIV_VIEW_COMMAND_ZOOM_IN: +		set_scale(self, self->scale * SCALE_STEP); +	break; case FASTIV_VIEW_COMMAND_ZOOM_OUT: +		set_scale(self, self->scale / SCALE_STEP); +	break; case FASTIV_VIEW_COMMAND_ZOOM_1: +		set_scale(self, 1.0); +	} +} diff --git a/fastiv-view.h b/fastiv-view.h index ca314f4..20ff445 100644 --- a/fastiv-view.h +++ b/fastiv-view.h @@ -24,3 +24,29 @@ G_DECLARE_FINAL_TYPE(FastivView, fastiv_view, FASTIV, VIEW, GtkWidget)  /// Try to open the given file, synchronously, to be displayed by the widget.  gboolean fastiv_view_open(FastivView *self, const gchar *path, GError **error); + +typedef enum _FastivViewCommand { +	FASTIV_VIEW_COMMAND_ROTATE_LEFT = 1, +	FASTIV_VIEW_COMMAND_MIRROR, +	FASTIV_VIEW_COMMAND_ROTATE_RIGHT, + +	FASTIV_VIEW_COMMAND_PAGE_FIRST, +	FASTIV_VIEW_COMMAND_PAGE_PREVIOUS, +	FASTIV_VIEW_COMMAND_PAGE_NEXT, +	FASTIV_VIEW_COMMAND_PAGE_LAST, + +	FASTIV_VIEW_COMMAND_FRAME_FIRST, +	FASTIV_VIEW_COMMAND_FRAME_PREVIOUS, +	FASTIV_VIEW_COMMAND_FRAME_NEXT, +	// Going to the end frame makes no sense, wrap around if needed. + +	FASTIV_VIEW_COMMAND_PRINT, +	FASTIV_VIEW_COMMAND_SAVE_PAGE, + +	FASTIV_VIEW_COMMAND_ZOOM_IN, +	FASTIV_VIEW_COMMAND_ZOOM_OUT, +	FASTIV_VIEW_COMMAND_ZOOM_1 +} FastivViewCommand; + +/// Execute a user action. +void fastiv_view_command(FastivView *self, FastivViewCommand command); @@ -19,10 +19,11 @@  #include <glib/gstdio.h>  #include <gtk/gtk.h> +#include <errno.h> +#include <math.h>  #include <stdarg.h>  #include <stdio.h>  #include <stdlib.h> -#include <errno.h>  #include <fnmatch.h> @@ -53,6 +54,53 @@ exit_fatal(const gchar *format, ...)  // --- Main -------------------------------------------------------------------- +// TODO(p): Add a toggle for a checkerboard background. +// TODO(p): Implement commented-out actions. +#define B make_toolbar_button +#define TOOLBAR(XX) \ +	XX(BROWSE,        B("view-grid-symbolic", "Browse")) \ +	XX(FILE_PREVIOUS, B("go-previous-symbolic", "Previous file")) \ +	XX(FILE_NEXT,     B("go-next-symbolic", "Next file")) \ +	XX(S1,            make_separator()) \ +	XX(PAGE_FIRST,    B("go-top-symbolic", "First page")) \ +	XX(PAGE_PREVIOUS, B("go-up-symbolic", "Previous page")) \ +	XX(PAGE_NEXT,     B("go-down-symbolic", "Next page")) \ +	XX(PAGE_LAST,     B("go-bottom-symbolic", "Last page")) \ +	XX(S2,            make_separator()) \ +	XX(SKIP_BACK,     B("media-skip-backward-symbolic", "Rewind playback")) \ +	XX(SEEK_BACK,     B("media-seek-backward-symbolic", "Previous frame")) \ +	/* TODO(p): The opposite is "media-playback-play-symbolic". */ \ +	/* XX(PAUSE,      B("media-playback-pause-symbolic", "Pause")) */ \ +	XX(SEEK_FORWARD,  B("media-seek-forward-symbolic", "Next frame")) \ +	XX(S3,            make_separator()) \ +	XX(PLUS,          B("zoom-in-symbolic", "Zoom in")) \ +	XX(SCALE,         gtk_label_new("100%")) \ +	XX(MINUS,         B("zoom-out-symbolic", "Zoom out")) \ +	XX(ONE,           B("zoom-original-symbolic", "Original size")) \ +	/* XX(FIT,        B("zoom-fit-best-symbolic", "Scale to fit")) */ \ +	XX(S4,            make_separator()) \ +	/* XX(PIN,        B("view-pin-symbolic", "Keep view configuration")) */ \ +	/* Or perhaps "blur-symbolic", also in the extended set. */ \ +	/* XX(SMOOTH,     B("blend-tool-symbolic", "Smooth scaling")) */ \ +	/* XX(COLOR,      B("preferences-color-symbolic", "Color management")) */ \ +	XX(SAVE,          B("document-save-as-symbolic", "Save as...")) \ +	XX(PRINT,         B("document-print-symbolic", "Print...")) \ +	/* XX(INFO,       B("info-symbolic", "Information")) */ \ +	XX(S5,            make_separator()) \ +	XX(LEFT,          B("object-rotate-left-symbolic", "Rotate left")) \ +	XX(MIRROR,        B("object-flip-horizontal-symbolic", "Mirror")) \ +	XX(RIGHT,         B("object-rotate-right-symbolic", "Rotate right")) \ +	XX(S6,            make_separator()) \ +	/* We are YouTube. */ \ +	XX(FULLSCREEN,    B("view-fullscreen-symbolic", "Fullscreen")) + +enum { +#define XX(id, constructor) TOOLBAR_ ## id, +	TOOLBAR(XX) +#undef XX +	TOOLBAR_COUNT +}; +  struct {  	gchar **supported_globs;  	gboolean filtering; @@ -65,14 +113,17 @@ struct {  	GtkWidget *window;  	GtkWidget *stack; -	GtkWidget *view; -	GtkWidget *view_scroller; -	GtkWidget *browser; -	GtkWidget *browser_scroller; +  	GtkWidget *browser_paned;  	GtkWidget *browser_sidebar;  	GtkWidget *plus;  	GtkWidget *minus; +	GtkWidget *browser_scroller; +	GtkWidget *browser; + +	GtkWidget *view_box; +	GtkWidget *toolbar[TOOLBAR_COUNT]; +	GtkWidget *view;  } g;  static gboolean @@ -119,7 +170,7 @@ static void  switch_to_view(const char *path)  {  	gtk_window_set_title(GTK_WINDOW(g.window), path); -	gtk_stack_set_visible_child(GTK_STACK(g.stack), g.view_scroller); +	gtk_stack_set_visible_child(GTK_STACK(g.stack), g.view_box);  	gtk_widget_grab_focus(g.view);  } @@ -395,6 +446,20 @@ toggle_fullscreen(void)  		gtk_window_fullscreen(GTK_WINDOW(g.window));  } +static void +on_window_state_event(G_GNUC_UNUSED GtkWidget *widget, +	GdkEventWindowState *event, G_GNUC_UNUSED gpointer user_data) +{ +	if (!(event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)) +		return; + +	const char *name = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) +		? "view-restore-symbolic" +		: "view-fullscreen-symbolic"; +	gtk_button_set_image(GTK_BUTTON(g.toolbar[TOOLBAR_FULLSCREEN]), +		gtk_image_new_from_icon_name(name, GTK_ICON_SIZE_BUTTON)); +} +  // Cursor keys, e.g., simply cannot be bound through accelerators  // (and GtkWidget::keynav-failed would arguably be an awful solution).  // @@ -530,6 +595,107 @@ on_button_press_browser(G_GNUC_UNUSED GtkWidget *widget, GdkEventButton *event)  	}  } +static GtkWidget * +make_toolbar_button(const gchar *symbolic, const gchar *tooltip) +{ +	GtkWidget *button = +		gtk_button_new_from_icon_name(symbolic, GTK_ICON_SIZE_BUTTON); +	gtk_widget_set_tooltip_text(button, tooltip); +//	gtk_widget_set_sensitive(button, FALSE); +	gtk_widget_set_focus_on_click(button, FALSE); + +	gtk_style_context_add_class( +		gtk_widget_get_style_context(button), GTK_STYLE_CLASS_FLAT); +	return button; +} + +static GtkWidget * +make_separator(void) +{ +	// TODO(p): See if it's possible to give the separator room to shrink +	// by some minor amount of pixels, margin-wise. +	GtkWidget *separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); +	gtk_widget_set_margin_start(separator, 10); +	gtk_widget_set_margin_end(separator, 10); +	return separator; +} + +static void +on_notify_scale( +	GObject *object, GParamSpec *param_spec, G_GNUC_UNUSED gpointer user_data) +{ +	double scale = 0; +	g_object_get(object, g_param_spec_get_name(param_spec), &scale, NULL); + +	gchar *scale_str = g_strdup_printf("%.0f%%", round(scale * 100)); +	gtk_label_set_text(GTK_LABEL(g.toolbar[TOOLBAR_SCALE]), scale_str); +	g_free(scale_str); +} + +static void +toolbar_connect(int index, GCallback callback) +{ +	g_signal_connect_swapped(g.toolbar[index], "clicked", callback, NULL); +} + +static void +on_command(intptr_t command) +{ +	fastiv_view_command(FASTIV_VIEW(g.view), command); +} + +static void +toolbar_command(int index, FastivViewCommand command) +{ +	g_signal_connect_swapped(g.toolbar[index], "clicked", +		G_CALLBACK(on_command), (void *) (intptr_t) command); +} + +// TODO(p): The toolbar should not be visible in fullscreen, +// or show up only when the cursor reaches the bottom of the screen. +// Presumably, GtkOverlay could be used for this. Proximity-based? +// Might want to make the toolbar normally translucent. +// TODO(p): The text and icons should be faded, unless the mouse cursor +// is on the toolbar. +static GtkWidget * +make_view_toolbar(void) +{ +#define XX(id, constructor) g.toolbar[TOOLBAR_ ## id] = constructor; +	TOOLBAR(XX) +#undef XX + +	// TODO(p): The scale should be at least as wide as "999%". +	gtk_widget_set_margin_start(g.toolbar[TOOLBAR_SCALE], 5); +	gtk_widget_set_margin_end(g.toolbar[TOOLBAR_SCALE], 5); + +	// GtkStatusBar solves a problem we do not have here. +	GtkWidget *view_toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); +	GtkBox *box = GTK_BOX(view_toolbar); +	for (int i = 0; i < TOOLBAR_COUNT; i++) +		gtk_box_pack_start(box, g.toolbar[i], FALSE, FALSE, 0); + +	toolbar_connect(TOOLBAR_BROWSE,        G_CALLBACK(switch_to_browser)); +	toolbar_connect(TOOLBAR_FILE_PREVIOUS, G_CALLBACK(on_previous)); +	toolbar_connect(TOOLBAR_FILE_NEXT,     G_CALLBACK(on_next)); +	toolbar_command(TOOLBAR_PAGE_FIRST,    FASTIV_VIEW_COMMAND_PAGE_FIRST); +	toolbar_command(TOOLBAR_PAGE_PREVIOUS, FASTIV_VIEW_COMMAND_PAGE_PREVIOUS); +	toolbar_command(TOOLBAR_PAGE_NEXT,     FASTIV_VIEW_COMMAND_PAGE_NEXT); +	toolbar_command(TOOLBAR_PAGE_LAST,     FASTIV_VIEW_COMMAND_PAGE_LAST); +	toolbar_command(TOOLBAR_SKIP_BACK,     FASTIV_VIEW_COMMAND_FRAME_FIRST); +	toolbar_command(TOOLBAR_SEEK_BACK,     FASTIV_VIEW_COMMAND_FRAME_PREVIOUS); +	toolbar_command(TOOLBAR_SEEK_FORWARD,  FASTIV_VIEW_COMMAND_FRAME_NEXT); +	toolbar_command(TOOLBAR_PLUS,          FASTIV_VIEW_COMMAND_ZOOM_IN); +	toolbar_command(TOOLBAR_MINUS,         FASTIV_VIEW_COMMAND_ZOOM_OUT); +	toolbar_command(TOOLBAR_ONE,           FASTIV_VIEW_COMMAND_ZOOM_1); +	toolbar_command(TOOLBAR_PRINT,         FASTIV_VIEW_COMMAND_PRINT); +	toolbar_command(TOOLBAR_SAVE,          FASTIV_VIEW_COMMAND_SAVE_PAGE); +	toolbar_command(TOOLBAR_LEFT,          FASTIV_VIEW_COMMAND_ROTATE_LEFT); +	toolbar_command(TOOLBAR_MIRROR,        FASTIV_VIEW_COMMAND_MIRROR); +	toolbar_command(TOOLBAR_RIGHT,         FASTIV_VIEW_COMMAND_ROTATE_RIGHT); +	toolbar_connect(TOOLBAR_FULLSCREEN,    G_CALLBACK(toggle_fullscreen)); +	return view_toolbar; +} +  int  main(int argc, char *argv[])  { @@ -578,10 +744,18 @@ main(int argc, char *argv[])  	// This is incredibly broken https://stackoverflow.com/a/51054396/76313  	// thus resolving the problem using overlaps. +	// XXX: button.flat is too generic, it's only for the view toolbar. +	// XXX: Similarly, box > separator.horizontal is a temporary hack. +	// Consider using a #name or a .class here, possibly for a parent widget.  	const char *style = "@define-color fastiv-tile #3c3c3c; \  		fastiv-view, fastiv-browser { background: @content_view_bg; } \  		placessidebar.fastiv .toolbar { padding: 2px 6px; } \  		placessidebar.fastiv box > separator { margin: 4px 0; } \ +		button.flat { padding-left: 0; padding-right: 0 } \ +		box > separator.horizontal { \ +			background: mix(@insensitive_fg_color, \ +				@insensitive_bg_color, 0.4); margin: 6px 0; \ +		} \  		fastiv-browser { padding: 5px; } \  		fastiv-browser.item { \  			border: 1px solid rgba(255, 255, 255, 0.375); \ @@ -606,23 +780,31 @@ main(int argc, char *argv[])  		GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);  	g_object_unref(provider); -	g.view_scroller = gtk_scrolled_window_new(NULL, NULL); +	GtkWidget *view_scroller = gtk_scrolled_window_new(NULL, NULL);  	g.view = g_object_new(FASTIV_TYPE_VIEW, NULL); -	gtk_widget_set_vexpand(g.view, TRUE); -	gtk_widget_set_hexpand(g.view, TRUE);  	g_signal_connect(g.view, "key-press-event",  		G_CALLBACK(on_key_press_view), NULL);  	g_signal_connect(g.view, "button-press-event",  		G_CALLBACK(on_button_press_view), NULL); -	gtk_container_add(GTK_CONTAINER(g.view_scroller), g.view); -	gtk_widget_show_all(g.view_scroller); +	g_signal_connect(g.view, "notify::scale", +		G_CALLBACK(on_notify_scale), NULL); +	gtk_container_add(GTK_CONTAINER(view_scroller), g.view);  	// Maybe our custom widgets should derive colours from the theme instead. -	gtk_scrolled_window_set_overlay_scrolling( -		GTK_SCROLLED_WINDOW(g.view_scroller), FALSE);  	g_object_set(gtk_settings_get_default(),  		"gtk-application-prefer-dark-theme", TRUE, NULL); +	GtkWidget *view_toolbar = make_view_toolbar(); +	gtk_widget_set_halign(view_toolbar, GTK_ALIGN_CENTER); + +	// Need to put the toolbar at the top, because of the horizontal scrollbar. +	g.view_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); +	gtk_box_pack_start(GTK_BOX(g.view_box), view_toolbar, FALSE, FALSE, 0); +	gtk_box_pack_start(GTK_BOX(g.view_box), +		gtk_separator_new(GTK_ORIENTATION_VERTICAL), FALSE, FALSE, 0); +	gtk_box_pack_start(GTK_BOX(g.view_box), view_scroller, TRUE, TRUE, 0); +	gtk_widget_show_all(g.view_box); +  	g.browser_scroller = gtk_scrolled_window_new(NULL, NULL);  	g.browser = g_object_new(FASTIV_TYPE_BROWSER, NULL);  	gtk_widget_set_vexpand(g.browser, TRUE); @@ -696,7 +878,7 @@ main(int argc, char *argv[])  	g.stack = gtk_stack_new();  	gtk_stack_set_transition_type(  		GTK_STACK(g.stack), GTK_STACK_TRANSITION_TYPE_NONE); -	gtk_container_add(GTK_CONTAINER(g.stack), g.view_scroller); +	gtk_container_add(GTK_CONTAINER(g.stack), g.view_box);  	gtk_container_add(GTK_CONTAINER(g.stack), g.browser_paned);  	g.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -704,6 +886,8 @@ main(int argc, char *argv[])  		G_CALLBACK(gtk_main_quit), NULL);  	g_signal_connect(g.window, "key-press-event",  		G_CALLBACK(on_key_press), NULL); +	g_signal_connect(g.window, "window-state-event", +		G_CALLBACK(on_window_state_event), NULL);  	gtk_container_add(GTK_CONTAINER(g.window), g.stack);  	char **types = fastiv_io_all_supported_media_types(); diff --git a/resources/blend-tool-symbolic.svg b/resources/blend-tool-symbolic.svg new file mode 100644 index 0000000..9ed84cd --- /dev/null +++ b/resources/blend-tool-symbolic.svg @@ -0,0 +1,154 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +    <filter id="a" height="100%" width="100%" x="0%" y="0%"> +        <feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/> +    </filter> +    <mask id="b"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.3"/> +        </g> +    </mask> +    <clipPath id="c"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <mask id="d"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/> +        </g> +    </mask> +    <clipPath id="e"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <mask id="f"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/> +        </g> +    </mask> +    <clipPath id="g"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <mask id="h"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/> +        </g> +    </mask> +    <clipPath id="i"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <mask id="j"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/> +        </g> +    </mask> +    <clipPath id="k"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <mask id="l"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/> +        </g> +    </mask> +    <clipPath id="m"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <mask id="n"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/> +        </g> +    </mask> +    <clipPath id="o"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <mask id="p"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.3"/> +        </g> +    </mask> +    <clipPath id="q"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <mask id="r"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.5"/> +        </g> +    </mask> +    <clipPath id="s"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <mask id="t"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.4"/> +        </g> +    </mask> +    <clipPath id="u"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <mask id="v"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.4"/> +        </g> +    </mask> +    <clipPath id="w"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <mask id="x"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.5"/> +        </g> +    </mask> +    <clipPath id="y"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <mask id="z"> +        <g filter="url(#a)"> +            <path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.5"/> +        </g> +    </mask> +    <clipPath id="A"> +        <path d="m 0 0 h 1024 v 800 h -1024 z"/> +    </clipPath> +    <g clip-path="url(#c)" mask="url(#b)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 562.460938 212.058594 h 10.449218 c -1.183594 0.492187 -1.296875 2.460937 0 3 h -10.449218 z m 0 0" fill="#2e3436"/> +    </g> +    <g clip-path="url(#e)" mask="url(#d)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 16 632 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/> +    </g> +    <g clip-path="url(#g)" mask="url(#f)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 17 631 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/> +    </g> +    <g clip-path="url(#i)" mask="url(#h)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 18 634 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/> +    </g> +    <g clip-path="url(#k)" mask="url(#j)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 16 634 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/> +    </g> +    <g clip-path="url(#m)" mask="url(#l)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 17 635 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/> +    </g> +    <g clip-path="url(#o)" mask="url(#n)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 19 635 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/> +    </g> +    <g fill="#2e3436"> +        <path d="m 1 1 v 14 h 14 v -14 z m 1 1 h 12 v 12 h -12 z m 0 0"/> +        <path d="m 6 11 h 1 v 1 h -1 z m 1 1 h 1 v 1 h -1 z m -1 -3 h 1 v 1 h -1 z m 1 1 h 1 v 1 h -1 z m -1 -3 h 1 v 1 h -1 z m 1 1 h 1 v 1 h -1 z m -1 -3 h 1 v 1 h -1 z m 1 1 h 1 v 1 h -1 z m -1 -3 h 1 v 1 h -1 z m 1 1 h 1 v 1 h -1 z m -4 -1 h 3 v 10 h -3 z m 0 0"/> +        <path d="m 8 3 h 1 v 10 h -1 z m 2 9 h 1 v 1 h -1 z m 0 -2 h 1 v 1 h -1 z m 0 -2 h 1 v 1 h -1 z m 0 -2 h 1 v 1 h -1 z m 0 -2 h 1 v 1 h -1 z m -1 7 h 1 v 1 h -1 z m 0 -2 h 1 v 1 h -1 z m 0 -2 h 1 v 1 h -1 z m 0 -2 h 1 v 1 h -1 z m 0 -2 h 1 v 1 h -1 z m 0 0" fill-opacity="0.524444"/> +    </g> +    <g clip-path="url(#q)" mask="url(#p)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 136 660 v 7 h 7 v -7 z m 0 0" fill="#2e3436"/> +    </g> +    <g clip-path="url(#s)" mask="url(#r)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 199 642 h 3 v 12 h -3 z m 0 0" fill="#2e3436"/> +    </g> +    <g clip-path="url(#u)" mask="url(#t)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 209.5 144.160156 c 0.277344 0 0.5 0.222656 0.5 0.5 v 1 c 0 0.277344 -0.222656 0.5 -0.5 0.5 s -0.5 -0.222656 -0.5 -0.5 v -1 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 z m 0 0" fill="#2e3436"/> +    </g> +    <g clip-path="url(#w)" mask="url(#v)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 206.5 144.160156 c 0.277344 0 0.5 0.222656 0.5 0.5 v 1 c 0 0.277344 -0.222656 0.5 -0.5 0.5 s -0.5 -0.222656 -0.5 -0.5 v -1 c 0 -0.277344 0.222656 -0.5 0.5 -0.5 z m 0 0" fill="#2e3436"/> +    </g> +    <g clip-path="url(#y)" mask="url(#x)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 229.5 143.160156 c -0.546875 0 -1 0.457032 -1 1 c 0 0.546875 0.453125 1 1 1 s 1 -0.453125 1 -1 c 0 -0.542968 -0.453125 -1 -1 -1 z m 0 0" fill="#2e3436"/> +    </g> +    <g clip-path="url(#A)" mask="url(#z)" transform="matrix(1 0 0 1 -96 -620)"> +        <path d="m 226.453125 143.160156 c -0.519531 0 -0.953125 0.433594 -0.953125 0.953125 v 0.09375 c 0 0.519531 0.433594 0.953125 0.953125 0.953125 h 0.09375 c 0.519531 0 0.953125 -0.433594 0.953125 -0.953125 v -0.09375 c 0 -0.519531 -0.433594 -0.953125 -0.953125 -0.953125 z m 0 0" fill="#2e3436"/> +    </g> +</svg> diff --git a/resources/resources.gresource.xml b/resources/resources.gresource.xml index b229292..ab85308 100644 --- a/resources/resources.gresource.xml +++ b/resources/resources.gresource.xml @@ -3,5 +3,6 @@  	<gresource prefix="/org/gnome/design/IconLibrary/">  		<file preprocess="xml-stripblanks">circle-filled-symbolic.svg</file>  		<file preprocess="xml-stripblanks">funnel-symbolic.svg</file> +		<file preprocess="xml-stripblanks">blend-tool-symbolic.svg</file>  	</gresource>  </gresources>  | 
