Compare commits

...

4 Commits

Author SHA1 Message Date
26a3c0c825
sdgui: bind ^W as in Readline 2021-10-17 11:58:06 +02:00
5216205056
README: make sdgui more of a first-class citizen 2021-10-17 11:25:32 +02:00
c364ec3b81
sdgui: stop hardcoding cell side padding 2021-10-17 11:18:29 +02:00
a31d329754
sdgui: stop hardcoding colours
Reusing colours from sdtui configuration would be awkward
and complicated, e.g. with font attributes, so abandon that idea.
2021-10-17 09:37:32 +02:00
4 changed files with 95 additions and 39 deletions

View File

@ -79,7 +79,7 @@ if (WITH_X11)
endif ()
pkg_check_modules (gtk gtk+-3.0)
option (WITH_GUI "Build a work-in-progress GTK+ UI" ${gtk_FOUND})
option (WITH_GUI "Build an alternative GTK+ UI" ${gtk_FOUND})
link_directories (${dependencies_LIBRARY_DIRS})
include_directories (${ZLIB_INCLUDE_DIRS} ${icu_INCLUDE_DIRS}

View File

@ -81,8 +81,9 @@ and general undesirability of terminal UIs, it might be better to start anew.
Graphical UI
------------
With GTK+ 3 development packages installed, an alternative, work-in-progress
frontend will be built and installed.
With GTK+ 3 development packages installed, an alternative frontend will be
built and installed. It shares the default dictionary list with 'sdtui',
but styling will follow your theme, and has to be customized from 'gtk.css'.
Contributing and Support
------------------------

View File

@ -246,8 +246,30 @@ main (int argc, char *argv[])
if (!load_dictionaries (g.dictionaries, &error))
die_with_dialog (error->message);
// Some Adwaita stupidity
const char *style = "notebook header tab { padding: 2px 8px; margin: 0; }";
// Some Adwaita stupidity, plus defaults for our own widget.
// All the named colours have been there since GNOME 3.4
// (see gnome-extra-themes git history, Adwaita used to live there).
const char *style = "notebook header tab { padding: 2px 8px; margin: 0; }"
// `gsettings set org.gnome.desktop.interface gtk-key-theme "Emacs"`
// isn't quite what I want, and note that ^U works by default
"@binding-set Readline {"
"bind '<Control>H' { 'delete-from-cursor' (chars, -1) };"
"bind '<Control>W' { 'delete-from-cursor' (word-ends, -1) }; }"
"entry { -gtk-key-bindings: Readline }"
"stardict-view { padding: 0 .25em; }"
"stardict-view.odd {"
"background: @theme_base_color; "
"color: @theme_text_color; }"
"stardict-view.odd:backdrop {"
"background: @theme_unfocused_base_color; "
"color: @theme_fg_color; /* should be more faded than 'text' */ }"
"stardict-view.even {"
"background: mix(@theme_base_color, @theme_text_color, 0.02); "
"color: @theme_text_color; }"
"stardict-view.even:backdrop {"
"background: mix(@theme_unfocused_base_color, "
"@theme_fg_color, 0.02); "
"color: @theme_fg_color; /* should be more faded than 'text' */ }";
GdkScreen *screen = gdk_screen_get_default ();
GtkCssProvider *provider = gtk_css_provider_new ();
@ -283,9 +305,6 @@ main (int argc, char *argv[])
// FIXME: when the clear icon shows, the widget changes in height
g.entry = gtk_search_entry_new ();
// TODO: attach to the "key-press-event" signal and implement ^W at least,
// though ^U is working already! Note that bindings can be done in CSS
// as well, if we have any extra specially for the editor
g_signal_connect (g.entry, "changed", G_CALLBACK (on_changed), g.view);
// TODO: make the entry have a background colour, rather than transparency
gtk_entry_set_has_frame (GTK_ENTRY (g.entry), FALSE);

View File

@ -136,24 +136,36 @@ view_entry_height (ViewEntry *ve, gint *word_offset, gint *defn_offset)
return MAX (word_y + word_h, defn_y + defn_h);
}
#define PADDING 5
static GtkBorder
view_entry_get_padding (GtkStyleContext *style)
{
GtkBorder padding = {};
GtkStateFlags state = gtk_style_context_get_state (style);
gtk_style_context_get_padding (style, state, &padding);
return padding;
}
static gint
view_entry_draw (ViewEntry *ve, cairo_t *cr, gint full_width, gboolean even)
view_entry_draw (ViewEntry *ve, cairo_t *cr, gint full_width,
GtkStyleContext *style)
{
// TODO: this shouldn't be hardcoded, read it out from somewhere
gdouble g = even ? 1. : .95;
gint word_y = 0, defn_y = 0,
height = view_entry_height (ve, &word_y, &defn_y);
cairo_rectangle (cr, 0, 0, full_width, height);
cairo_set_source_rgb (cr, g, g, g);
cairo_fill (cr);
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_move_to (cr, full_width / 2 + PADDING, defn_y);
pango_cairo_show_layout (cr, ve->definition_layout);
gtk_render_background (style, cr, 0, 0, full_width, height);
gtk_render_frame (style, cr, 0, 0, full_width, height);
// Top/bottom and left/right-dependent padding will not work, too much code
GtkBorder padding = view_entry_get_padding (style);
gtk_style_context_save (style);
gtk_style_context_add_class (style, GTK_STYLE_CLASS_RIGHT);
gtk_render_layout (style, cr,
full_width / 2 + padding.left, defn_y, ve->definition_layout);
gtk_style_context_restore (style);
gtk_style_context_save (style);
gtk_style_context_add_class (style, GTK_STYLE_CLASS_LEFT);
PangoLayoutIter *iter = pango_layout_get_iter (ve->definition_layout);
do
{
@ -162,36 +174,43 @@ view_entry_draw (ViewEntry *ve, cairo_t *cr, gint full_width, gboolean even)
PangoRectangle logical = {};
pango_layout_iter_get_line_extents (iter, NULL, &logical);
cairo_move_to (cr, PADDING, word_y + PANGO_PIXELS (logical.y));
pango_cairo_show_layout (cr, ve->word_layout);
gtk_render_layout (style, cr,
padding.left, word_y + PANGO_PIXELS (logical.y), ve->word_layout);
}
while (pango_layout_iter_next_line (iter));
pango_layout_iter_free (iter);
gtk_style_context_restore (style);
return height;
}
static void
view_entry_rebuild_layout (ViewEntry *ve, PangoContext *pc, gint width)
view_entry_rebuild_layouts (ViewEntry *ve, GtkWidget *widget)
{
PangoContext *pc = gtk_widget_get_pango_context (widget);
GtkStyleContext *style = gtk_widget_get_style_context (widget);
gint full_width = gtk_widget_get_allocated_width (widget);
g_clear_object (&ve->word_layout);
g_clear_object (&ve->definition_layout);
int left_width = width / 2 - 2 * PADDING;
int right_width = width - left_width - 2 * PADDING;
if (left_width < 1 || right_width < 1)
GtkBorder padding = view_entry_get_padding (style);
gint part_width = full_width / 2 - padding.left - padding.right;
if (part_width < 1)
return;
// Left/right-dependent fonts aren't supported (GTK_STYLE_PROPERTY_FONT)
// TODO: preferably pre-validate the layouts with pango_parse_markup(),
// so that it doesn't warn without indication on the frontend
ve->word_layout = pango_layout_new (pc);
pango_layout_set_markup (ve->word_layout, ve->word, -1);
pango_layout_set_ellipsize (ve->word_layout, PANGO_ELLIPSIZE_END);
pango_layout_set_single_paragraph_mode (ve->word_layout, TRUE);
pango_layout_set_width (ve->word_layout, PANGO_SCALE * left_width);
pango_layout_set_width (ve->word_layout, PANGO_SCALE * part_width);
ve->definition_layout = pango_layout_new (pc);
pango_layout_set_markup (ve->definition_layout, ve->definition, -1);
pango_layout_set_width (ve->definition_layout, PANGO_SCALE * right_width);
pango_layout_set_width (ve->definition_layout, PANGO_SCALE * part_width);
pango_layout_set_wrap (ve->definition_layout, PANGO_WRAP_WORD_CHAR);
}
// --- Widget ------------------------------------------------------------------
@ -214,12 +233,9 @@ struct _StardictView
static ViewEntry *
make_entry (StardictView *self, StardictIterator *iterator)
{
ViewEntry *ve =
view_entry_new (iterator, self->matched ? self->matched : "");
GtkWidget *widget = GTK_WIDGET (self);
view_entry_rebuild_layout (ve, gtk_widget_get_pango_context (widget),
gtk_widget_get_allocated_width (widget));
const gchar *matched = self->matched ? self->matched : "";
ViewEntry *ve = view_entry_new (iterator, matched);
view_entry_rebuild_layouts (ve, GTK_WIDGET (self));
return ve;
}
@ -354,10 +370,12 @@ stardict_view_get_preferred_height (GtkWidget *widget,
}
static void
stardict_view_get_preferred_width (GtkWidget *widget G_GNUC_UNUSED,
stardict_view_get_preferred_width (GtkWidget *widget,
gint *minimum, gint *natural)
{
*natural = *minimum = 4 * PADDING;
GtkStyleContext *style = gtk_widget_get_style_context (widget);
GtkBorder padding = view_entry_get_padding (style);
*natural = *minimum = 2 * (padding.left + 1 * padding.right);
}
static void
@ -387,9 +405,7 @@ stardict_view_realize (GtkWidget *widget)
GdkWindow *window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes, GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL);
// The default background colour of the GDK window is transparent,
// we'll keep it that way, rather than apply the style context.
// The default background colour of the GDK window is transparent
gtk_widget_register_window (widget, window);
gtk_widget_set_window (widget, window);
gtk_widget_set_realized (widget, TRUE);
@ -403,15 +419,33 @@ stardict_view_draw (GtkWidget *widget, cairo_t *cr)
GtkAllocation allocation;
gtk_widget_get_allocation (widget, &allocation);
GtkStyleContext *style = gtk_widget_get_style_context (widget);
gtk_render_background (style, cr,
0, 0, allocation.width, allocation.height);
gtk_render_frame (style, cr,
0, 0, allocation.width, allocation.height);
gint offset = -self->top_offset;
gint i = self->top_position;
for (GList *iter = self->entries; iter; iter = iter->next)
{
// Style regions would be appropriate, if they weren't deprecated.
// GTK+ CSS gadgets/notes are an internal API. We don't want to turn
// this widget into a container, to avoid needless complexity.
//
// gtk_style_context_{get,set}_path() may be misused by adding the same
// GType with gtk_widget_path_append_type() and changing its name
// using gtk_widget_path_iter_set_name()... but that is ugly.
gtk_style_context_save (style);
gtk_style_context_add_class (style, (i++ & 1) ? "even" : "odd");
cairo_save (cr);
cairo_translate (cr, 0, offset);
// TODO: later exclude clipped entries, but it's not that important
offset += view_entry_draw (iter->data, cr, allocation.width, i++ & 1);
offset += view_entry_draw (iter->data, cr, allocation.width, style);
cairo_restore (cr);
gtk_style_context_restore (style);
}
return TRUE;
}
@ -476,6 +510,8 @@ stardict_view_class_init (StardictViewClass *klass)
widget_class->size_allocate = stardict_view_size_allocate;
widget_class->screen_changed = stardict_view_screen_changed;
widget_class->scroll_event = stardict_view_scroll_event;
gtk_widget_class_set_css_name (widget_class, "stardict-view");
}
static void