diff options
author | Christian Persch <chpe@src.gnome.org> | 2021-03-13 22:10:42 +0100 |
---|---|---|
committer | Christian Persch <chpe@src.gnome.org> | 2021-03-13 22:10:42 +0100 |
commit | 1be3d3d9fa160874ccf162acd1adb88ca8ba05a8 (patch) | |
tree | 10ca744c6d74b55b7275a0bb59262ff20ab4e2f1 | |
parent | cf4984c4c35b6c15b239fbea5abf3a92271665d2 (diff) | |
download | vte-1be3d3d9fa160874ccf162acd1adb88ca8ba05a8.tar.gz |
widget: Add alignment properties
Add API to set how to distribute extra-grid space allocated to the
widget. Previously, vte always positioned the content at the left and top
of the allocation.
Fixes: https://gitlab.gnome.org/GNOME/vte/-/issues/337
-rw-r--r-- | doc/reference/vte-sections.txt.in | 4 | ||||
-rw-r--r-- | src/app/app.cc | 33 | ||||
-rw-r--r-- | src/vte.cc | 127 | ||||
-rw-r--r-- | src/vte/vteterminal.h | 14 | ||||
-rw-r--r-- | src/vtedefines.hh | 3 | ||||
-rw-r--r-- | src/vtegtk.cc | 144 | ||||
-rw-r--r-- | src/vtegtk.hh | 2 | ||||
-rw-r--r-- | src/vteinternal.hh | 22 | ||||
-rw-r--r-- | src/widget.cc | 8 | ||||
-rw-r--r-- | src/widget.hh | 28 |
10 files changed, 325 insertions, 60 deletions
diff --git a/doc/reference/vte-sections.txt.in b/doc/reference/vte-sections.txt.in index 7e735f5b..3f6cd2bb 100644 --- a/doc/reference/vte-sections.txt.in +++ b/doc/reference/vte-sections.txt.in @@ -103,6 +103,10 @@ vte_terminal_search_set_wrap_around vte_terminal_event_check_regex_array vte_terminal_event_check_regex_simple #endif /* VTE_GTK */ +vte_terminal_get_xalign +vte_terminal_set_xalign +vte_terminal_get_yalign +vte_terminal_set_yalign <SUBSECTION> VteFeatureFlags diff --git a/src/app/app.cc b/src/app/app.cc index 1c772718..2712ca52 100644 --- a/src/app/app.cc +++ b/src/app/app.cc @@ -122,6 +122,8 @@ public: VteCursorBlinkMode cursor_blink_mode{VTE_CURSOR_BLINK_SYSTEM}; VteCursorShape cursor_shape{VTE_CURSOR_SHAPE_BLOCK}; VteTextBlinkMode text_blink_mode{VTE_TEXT_BLINK_ALWAYS}; + GtkAlign xalign{GtkAlign(-1)}; + GtkAlign yalign{GtkAlign(-1)}; vte::glib::RefPtr<GtkCssProvider> css{}; #if VTE_GTK == 3 @@ -476,6 +478,28 @@ private: return true; } + static gboolean + parse_xalign(char const* option, char const* value, void* data, GError** error) + { + auto const that = static_cast<Options*>(data); + auto v = int{}; + auto const rv = that->parse_enum(GTK_TYPE_ALIGN, value, v, error); + if (rv) + that->xalign = GtkAlign(v); + return rv; + } + + static gboolean + parse_yalign(char const* option, char const* value, void* data, GError** error) + { + auto const that = static_cast<Options*>(data); + auto v = int{}; + auto const rv = that->parse_enum(GTK_TYPE_ALIGN, value, v, error); + if (rv) + that->yalign = GtkAlign(v); + return rv; + } + public: double get_alpha() const @@ -668,6 +692,11 @@ public: { "use-theme-colors", 0, 0, G_OPTION_ARG_NONE, &use_theme_colors, "Use foreground and background colors from the gtk+ theme", nullptr }, + { "xalign", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_xalign, + "Horizontal alignment (fill|start|end|center)", "ALIGN" }, + { "yalign", 0, 0, G_OPTION_ARG_CALLBACK, (void*)parse_yalign, + "Vertical alignment (fill|start|end|center)", "ALIGN" }, + #if VTE_GTK == 3 { "no-argb-visual", 0, 0, G_OPTION_ARG_NONE, &no_argb_visual, "Don't use an ARGB visual", nullptr }, @@ -2549,6 +2578,10 @@ vteapp_window_constructed(GObject *object) vte_terminal_set_scroll_unit_is_pixels(window->terminal, options.scroll_unit_is_pixels); vte_terminal_set_scrollback_lines(window->terminal, options.scrollback_lines); vte_terminal_set_text_blink_mode(window->terminal, options.text_blink_mode); + if (options.xalign != GtkAlign(-1)) + vte_terminal_set_xalign(window->terminal, options.xalign); + if (options.yalign != GtkAlign(-1)) + vte_terminal_set_yalign(window->terminal, options.yalign); /* Style */ if (options.font_string != nullptr) { @@ -4447,16 +4447,15 @@ Terminal::set_border_padding(GtkBorder const* padding) { if (memcmp(padding, &m_padding, sizeof(*padding)) != 0) { _vte_debug_print(VTE_DEBUG_MISC | VTE_DEBUG_WIDGET_SIZE, - "Setting padding to (%d,%d,%d,%d)\n", + "Setting style padding to (%d,%d,%d,%d)\n", padding->left, padding->right, padding->top, padding->bottom); - m_padding = *padding; - update_view_extents(); + m_style_padding = *padding; gtk_widget_queue_resize(m_widget); } else { _vte_debug_print(VTE_DEBUG_MISC | VTE_DEBUG_WIDGET_SIZE, - "Keeping padding the same at (%d,%d,%d,%d)\n", + "Keeping style padding the same at (%d,%d,%d,%d)\n", padding->left, padding->right, padding->top, padding->bottom); @@ -7762,13 +7761,11 @@ Terminal::widget_measure_width(int *minimum_width, refresh_size(); - *minimum_width = m_cell_width * 2; /* have room for a CJK or emoji */ + *minimum_width = m_cell_width * VTE_MIN_GRID_WIDTH; *natural_width = m_cell_width * m_column_count; - *minimum_width += m_padding.left + - m_padding.right; - *natural_width += m_padding.left + - m_padding.right; + *minimum_width += m_style_padding.left + m_style_padding.right; + *natural_width += m_style_padding.left + m_style_padding.right; _vte_debug_print(VTE_DEBUG_WIDGET_SIZE, "[Terminal %p] minimum_width=%d, natural_width=%d for %ldx%ld cells (padding %d,%d;%d,%d).\n", @@ -7776,7 +7773,8 @@ Terminal::widget_measure_width(int *minimum_width, *minimum_width, *natural_width, m_column_count, m_row_count, - m_padding.left, m_padding.right, m_padding.top, m_padding.bottom); + m_style_padding.left, m_style_padding.right, + m_style_padding.top, m_style_padding.bottom); } void @@ -7787,13 +7785,11 @@ Terminal::widget_measure_height(int *minimum_height, refresh_size(); - *minimum_height = m_cell_height * 1; + *minimum_height = m_cell_height * VTE_MIN_GRID_HEIGHT; *natural_height = m_cell_height * m_row_count; - *minimum_height += m_padding.top + - m_padding.bottom; - *natural_height += m_padding.top + - m_padding.bottom; + *minimum_height += m_style_padding.top + m_style_padding.bottom; + *natural_height += m_style_padding.top + m_style_padding.bottom; _vte_debug_print(VTE_DEBUG_WIDGET_SIZE, "[Terminal %p] minimum_height=%d, natural_height=%d for %ldx%ld cells (padding %d,%d;%d,%d).\n", @@ -7801,7 +7797,8 @@ Terminal::widget_measure_height(int *minimum_height, *minimum_height, *natural_height, m_column_count, m_row_count, - m_padding.left, m_padding.right, m_padding.top, m_padding.bottom); + m_style_padding.left, m_style_padding.right, + m_style_padding.top, m_style_padding.bottom); } void @@ -7810,38 +7807,71 @@ Terminal::widget_size_allocate(int allocation_x, int allocation_y, int allocation_width, int allocation_height, - int allocation_baseline) + int allocation_baseline, + Alignment xalign, + Alignment yalign) #elif VTE_GTK == 4 Terminal::widget_size_allocate(int allocation_width, int allocation_height, - int allocation_baseline) + int allocation_baseline, + Alignment xalign, + Alignment yalign) #endif /* VTE_GTK */ { - glong width, height; - gboolean repaint, update_scrollback; + auto width = allocation_width - (m_style_padding.left + m_style_padding.right); + auto height = allocation_height - (m_style_padding.top + m_style_padding.bottom); - _vte_debug_print(VTE_DEBUG_LIFECYCLE, - "vte_terminal_size_allocate()\n"); + auto grid_width = int(width / m_cell_width); + auto grid_height = int(height / m_cell_height); - width = (allocation_width - (m_padding.left + m_padding.right)) / - m_cell_width; - height = (allocation_height - (m_padding.top + m_padding.bottom)) / - m_cell_height; - width = MAX(width, 1); - height = MAX(height, 1); + width -= grid_width * m_cell_width; + height -= grid_height * m_cell_height; + /* assert(width >= 0); assert(height >= 0); */ - _vte_debug_print(VTE_DEBUG_WIDGET_SIZE, - "[Terminal %p] Sizing window to %dx%d (%ldx%ld, padding %d,%d;%d,%d).\n", - m_terminal, - allocation_width, allocation_height, - width, height, - m_padding.left, m_padding.right, m_padding.top, m_padding.bottom); + /* Distribute extra space according to alignment */ + auto lpad = 0, rpad = 0; + switch (xalign) { + default: + case Alignment::FILL: + case Alignment::BASELINE: + case Alignment::START: lpad = 0; rpad = width; break; + case Alignment::END: lpad = width; rpad = 0; break; + case Alignment::CENTRE: lpad = width / 2; rpad = width - lpad; break; + } + + auto tpad = 0, bpad = 0; + switch (yalign) { + default: + case Alignment::FILL: tpad = bpad = 0; break; + case Alignment::BASELINE: + case Alignment::START: tpad = 0; bpad = height; break; + case Alignment::END: tpad = height; bpad = 0; break; + case Alignment::CENTRE: tpad = height / 2; bpad = height - tpad; break; + } - auto current_allocation = get_allocated_rect(); + m_padding.left = m_style_padding.left + lpad; + m_padding.right = m_style_padding.right + rpad; + m_padding.top = m_style_padding.top + tpad; + m_padding.bottom = m_style_padding.bottom + bpad; - repaint = current_allocation.width != allocation_width || + /* The minimum size returned from ::widget_measure_width/height() + * is VTE_MIN_GRID_WIDTH/HEIGHT, but let's be extra safe. + */ + grid_width = std::max(grid_width, VTE_MIN_GRID_WIDTH); + grid_height = std::max(grid_height, VTE_MIN_GRID_HEIGHT); + + _vte_debug_print(VTE_DEBUG_WIDGET_SIZE, + "[Terminal %p] Sizing window to %dx%d (%dx%d, effective padding %d,%d;%d,%d).\n", + m_terminal, + allocation_width, allocation_height, + grid_width, grid_height, + m_padding.left, m_padding.right, m_padding.top, m_padding.bottom); + + auto const current_allocation = get_allocated_rect(); + auto const repaint = current_allocation.width != allocation_width || current_allocation.height != allocation_height; - update_scrollback = current_allocation.height != allocation_height; + /* FIXME: remove this */ + auto const update_scrollback = current_allocation.height != allocation_height; #if VTE_GTK == 3 set_allocated_rect({allocation_x, allocation_y, allocation_width, allocation_height}); @@ -7849,24 +7879,21 @@ Terminal::widget_size_allocate(int allocation_width, set_allocated_rect({0, 0, allocation_width, allocation_height}); #endif - if (width != m_column_count - || height != m_row_count - || update_scrollback) - { - /* Set the size of the pseudo-terminal. */ - set_size(width, height); + if (grid_width != m_column_count || + grid_height != m_row_count || + update_scrollback) { + /* Set the size of the pseudo-terminal. */ + set_size(grid_width, grid_height); /* Notify viewers that the contents have changed. */ queue_contents_changed(); } - if (widget_realized()) { - /* Force a repaint if we were resized. */ - if (repaint) { - reset_update_rects(); - invalidate_all(); - } - } + /* Force a repaint if we were resized. */ + if (widget_realized() && repaint) { + reset_update_rects(); + invalidate_all(); + } } void diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h index 05f6674f..a3cfc307 100644 --- a/src/vte/vteterminal.h +++ b/src/vte/vteterminal.h @@ -545,6 +545,20 @@ void vte_terminal_set_enable_sixel(VteTerminal *terminal, _VTE_PUBLIC gboolean vte_terminal_get_enable_sixel(VteTerminal *terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); +_VTE_PUBLIC +void vte_terminal_set_xalign(VteTerminal* terminal, + GtkAlign align) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); + +_VTE_PUBLIC +GtkAlign vte_terminal_get_xalign(VteTerminal* terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); + +_VTE_PUBLIC +void vte_terminal_set_yalign(VteTerminal* terminal, + GtkAlign align) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); + +_VTE_PUBLIC +GtkAlign vte_terminal_get_yalign(VteTerminal* terminal) _VTE_CXX_NOEXCEPT _VTE_GNUC_NONNULL(1); + G_DEFINE_AUTOPTR_CLEANUP_FUNC(VteTerminal, g_object_unref) G_END_DECLS diff --git a/src/vtedefines.hh b/src/vtedefines.hh index 16f6ae78..fe8d6e97 100644 --- a/src/vtedefines.hh +++ b/src/vtedefines.hh @@ -151,3 +151,6 @@ #define VTE_MIN_CURSOR_BLINK_CYCLE (50 /* ms */) #define VTE_MIN_CURSOR_BLINK_TIMEOUT (50 /* ms */) + +#define VTE_MIN_GRID_WIDTH (2) /* space for one wide character */ +#define VTE_MIN_GRID_HEIGHT (1) diff --git a/src/vtegtk.cc b/src/vtegtk.cc index 900c9cfb..5714a5b7 100644 --- a/src/vtegtk.cc +++ b/src/vtegtk.cc @@ -1012,7 +1012,15 @@ try g_value_set_string (value, vte_terminal_get_word_char_exceptions (terminal)); break; - default: + case PROP_XALIGN: + g_value_set_enum(value, vte_terminal_get_xalign(terminal)); + break; + + case PROP_YALIGN: + g_value_set_enum(value, vte_terminal_get_yalign(terminal)); + break; + + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); return; } @@ -1130,6 +1138,14 @@ try vte_terminal_set_word_char_exceptions (terminal, g_value_get_string (value)); break; + case PROP_XALIGN: + vte_terminal_set_xalign(terminal, GtkAlign(g_value_get_enum(value))); + break; + + case PROP_YALIGN: + vte_terminal_set_yalign(terminal, GtkAlign(g_value_get_enum(value))); + break; + /* Not writable */ case PROP_CURRENT_DIRECTORY_URI: case PROP_CURRENT_FILE_URI: @@ -2328,6 +2344,32 @@ vte_terminal_class_init(VteTerminalClass *klass) NULL, (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); + /** + * VteTerminal:xalign: + * + * The horizontal alignment of @terminal within its allocation. + * + * Since: 0.66 + */ + pspecs[PROP_XALIGN] = + g_param_spec_enum("xalign", nullptr, nullptr, + GTK_TYPE_ALIGN, + GTK_ALIGN_FILL, + GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); + + /** + * VteTerminal:yalign: + * + * The vertical alignment of @terminal within its allocation + * + * Since: 0.66 + */ + pspecs[PROP_YALIGN] = + g_param_spec_enum("yalign", nullptr, nullptr, + GTK_TYPE_ALIGN, + GTK_ALIGN_FILL, + GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY)); + g_object_class_install_properties(gobject_class, LAST_PROP, pspecs); #if VTE_GTK == 3 @@ -6142,6 +6184,106 @@ catch (...) return false; } +/** + * vte_terminal_set_xalign: + * @terminal: a #VteTerminal + * @align: alignment value from #GtkAlign + * + * Sets the horizontal alignment of @terminal within its allocation. + * + * Note: %GTK_ALIGN_FILL and %GTK_ALIGN_BASELINE are not supported and + * will be treated like %GTK_ALIGN_START. + * + * Since: 0.66 + */ +void +vte_terminal_set_xalign(VteTerminal* terminal, + GtkAlign align) noexcept +try +{ + g_return_if_fail(VTE_IS_TERMINAL(terminal)); + g_return_if_fail(align >= GTK_ALIGN_FILL && align <= GTK_ALIGN_BASELINE); + + if (WIDGET(terminal)->set_xalign(align)) + g_object_notify_by_pspec(G_OBJECT(terminal), pspecs[PROP_XALIGN]); +} +catch (...) +{ + vte::log_exception(); +} + +/** + * vte_terminal_get_xalign: + * @terminal: a #VteTerminal + * + * Returns: the horizontal alignment of @terminal within its allocation + * + * Since: 0.66 + */ +GtkAlign +vte_terminal_get_xalign(VteTerminal* terminal) noexcept +try +{ + g_return_val_if_fail(VTE_IS_TERMINAL(terminal), GTK_ALIGN_START); + + return WIDGET(terminal)->xalign(); +} +catch (...) +{ + vte::log_exception(); + return GTK_ALIGN_FILL; +} + +/** + * vte_terminal_set_yalign: + * @terminal: a #VteTerminal + * @align: alignment value from #GtkAlign + * + * Sets the vertical alignment of @terminal within its allocation. + * + * Note: %GTK_ALIGN_BASELINE is not supported and will be treated like + * %GTK_ALIGN_START. + * + * Since: 0.66 + */ +void +vte_terminal_set_yalign(VteTerminal* terminal, + GtkAlign align) noexcept +try +{ + g_return_if_fail(VTE_IS_TERMINAL(terminal)); + g_return_if_fail(align >= GTK_ALIGN_FILL && align <= GTK_ALIGN_BASELINE); + + if (WIDGET(terminal)->set_yalign(align)) + g_object_notify_by_pspec(G_OBJECT(terminal), pspecs[PROP_YALIGN]); +} +catch (...) +{ + vte::log_exception(); +} + +/** + * vte_terminal_get_yalign: + * @terminal: a #VteTerminal + * + * Returns: the vertical alignment of @terminal within its allocation + * + * Since: 0.66 + */ +GtkAlign +vte_terminal_get_yalign(VteTerminal* terminal) noexcept +try +{ + g_return_val_if_fail(VTE_IS_TERMINAL(terminal), GTK_ALIGN_FILL); + + return WIDGET(terminal)->yalign(); +} +catch (...) +{ + vte::log_exception(); + return GTK_ALIGN_FILL; +} + namespace vte { using namespace std::literals; diff --git a/src/vtegtk.hh b/src/vtegtk.hh index 6b7a1ea2..e32430be 100644 --- a/src/vtegtk.hh +++ b/src/vtegtk.hh @@ -92,6 +92,8 @@ enum { PROP_TEXT_BLINK_MODE, PROP_WINDOW_TITLE, PROP_WORD_CHAR_EXCEPTIONS, + PROP_XALIGN, + PROP_YALIGN, LAST_PROP, /* override properties */ diff --git a/src/vteinternal.hh b/src/vteinternal.hh index 9e5eabd8..a9ca86b3 100644 --- a/src/vteinternal.hh +++ b/src/vteinternal.hh @@ -209,6 +209,14 @@ private: eLINE, }; + enum class Alignment : uint8_t { + FILL, + START, + END, + CENTRE, + BASELINE + }; + protected: /* NOTE: This needs to be kept in sync with the public VteCursorBlinkMode enum */ @@ -725,11 +733,11 @@ public: /* Style stuff */ #if VTE_GTK == 3 - GtkBorder m_padding{1, 1, 1, 1}; + GtkBorder m_style_padding{1, 1, 1, 1}; #elif VTE_GTK == 4 - GtkBorder m_padding{0, 0, 0, 0}; + GtkBorder m_style_padding{0, 0, 0, 0}; #endif - auto padding() const noexcept { return &m_padding; } + GtkBorder m_padding{m_style_padding}; /* Hyperlinks */ bool m_allow_hyperlink{false}; @@ -923,11 +931,15 @@ public: int y, int width, int height, - int baseline); + int baseline, + Alignment xalign, + Alignment yalign); #elif VTE_GTK == 4 void widget_size_allocate(int width, int height, - int baseline); + int baseline, + Alignment xalign, + Alignment yalign); #endif /* VTE_GTK */ void set_blink_settings(bool blink, diff --git a/src/widget.cc b/src/widget.cc index 233f36fc..7adc7031 100644 --- a/src/widget.cc +++ b/src/widget.cc @@ -1405,7 +1405,9 @@ Widget::size_allocate(GtkAllocation* allocation) m_terminal->widget_size_allocate(allocation->x, allocation->y, allocation->width, allocation->height, - -1); + -1, + vte::terminal::Terminal::Alignment(m_xalign), + vte::terminal::Terminal::Alignment(m_yalign)); gtk_widget_set_allocation(gtk(), allocation); @@ -1427,7 +1429,9 @@ Widget::size_allocate(int width, _vte_debug_print(VTE_DEBUG_WIDGET_SIZE, "Widget size allocate width=%d height=%d baseline=%d\n", width, height, baseline); - terminal()->widget_size_allocate(width, height, baseline); + terminal()->widget_size_allocate(width, height, baseline, + vte::terminal::Terminal::Alignment(m_xalign), + vte::terminal::Terminal::Alignment(m_yalign)); gtk_widget_allocate(gtk(), width, height, baseline, nullptr); } diff --git a/src/widget.hh b/src/widget.hh index 277ca1ea..19aaf733 100644 --- a/src/widget.hh +++ b/src/widget.hh @@ -373,8 +373,6 @@ public: constexpr auto scroll_unit_is_pixels() const noexcept { return m_scroll_unit_is_pixels; } - auto padding() const noexcept { return terminal()->padding(); } - bool set_cursor_blink_mode(VteCursorBlinkMode mode) { return terminal()->set_cursor_blink_mode(vte::terminal::Terminal::CursorBlinkMode(mode)); } auto cursor_blink_mode() const noexcept { return VteCursorBlinkMode(terminal()->cursor_blink_mode()); } @@ -446,6 +444,29 @@ public: bool set_sixel_enabled(bool enabled) noexcept { return m_terminal->set_sixel_enabled(enabled); } bool sixel_enabled() const noexcept { return m_terminal->sixel_enabled(); } + constexpr auto xalign() const noexcept { return m_xalign; } + constexpr auto yalign() const noexcept { return m_yalign; } + + bool set_xalign(GtkAlign align) noexcept + { + if (align == m_xalign) + return false; + + m_xalign = align; + gtk_widget_queue_allocate(gtk()); + return true; + } + + bool set_yalign(GtkAlign align) noexcept + { + if (align == m_yalign) + return false; + + m_yalign = align; + gtk_widget_queue_allocate(gtk()); + return true; + } + protected: enum class CursorType { @@ -549,6 +570,9 @@ private: unsigned m_vscroll_policy:1{GTK_SCROLL_NATURAL}; unsigned m_scroll_unit_is_pixels:1{false}; unsigned m_changing_scroll_position:1{false}; + + GtkAlign m_xalign{GTK_ALIGN_START}; + GtkAlign m_yalign{GTK_ALIGN_FILL}; }; } // namespace platform |