Commit 4005f653 authored by Egmont Koblinger's avatar Egmont Koblinger

widget: Rewrite the tracking of selection endpoints

Track mouse click and drag positions as pointing to the left or right half
of a character cell, rather than between cells. This change is required for
forthcoming BiDi support.

Refactor the methods and their responsibilities.

Fixes and improvements of corner cases as listed in the bugreport.

#34
parent 1d4eab2e
This diff is collapsed.
...@@ -1421,14 +1421,11 @@ vte_terminal_accessible_get_selection(AtkText *text, gint selection_number, ...@@ -1421,14 +1421,11 @@ vte_terminal_accessible_get_selection(AtkText *text, gint selection_number,
auto impl = IMPL_FROM_WIDGET(widget); auto impl = IMPL_FROM_WIDGET(widget);
if (!impl->m_has_selection || impl->m_selection[VTE_SELECTION_PRIMARY] == nullptr) if (impl->m_selection_resolved.empty() || impl->m_selection[VTE_SELECTION_PRIMARY] == nullptr)
return NULL; return NULL;
auto start_sel = impl->m_selection_start; *start_offset = offset_from_xy (priv, impl->m_selection_resolved.start_column(), impl->m_selection_resolved.start_row());
auto end_sel = impl->m_selection_end; *end_offset = offset_from_xy (priv, impl->m_selection_resolved.end_column(), impl->m_selection_resolved.end_row());
*start_offset = offset_from_xy (priv, start_sel.col, start_sel.row);
*end_offset = offset_from_xy (priv, end_sel.col, end_sel.row);
return g_strdup(impl->m_selection[VTE_SELECTION_PRIMARY]->str); return g_strdup(impl->m_selection[VTE_SELECTION_PRIMARY]->str);
} }
......
...@@ -3918,7 +3918,7 @@ gboolean ...@@ -3918,7 +3918,7 @@ gboolean
vte_terminal_get_has_selection(VteTerminal *terminal) vte_terminal_get_has_selection(VteTerminal *terminal)
{ {
g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE); g_return_val_if_fail(VTE_IS_TERMINAL(terminal), FALSE);
return IMPL(terminal)->m_has_selection; return !IMPL(terminal)->m_selection_resolved.empty();
} }
/** /**
......
...@@ -423,22 +423,19 @@ public: ...@@ -423,22 +423,19 @@ public:
std::u32string m_word_char_exceptions; std::u32string m_word_char_exceptions;
/* Selection information. */ /* Selection information. */
gboolean m_has_selection;
gboolean m_selecting; gboolean m_selecting;
gboolean m_selecting_after_threshold; gboolean m_will_select_after_threshold;
gboolean m_selecting_restart;
gboolean m_selecting_had_delta; gboolean m_selecting_had_delta;
gboolean m_selection_block_mode; gboolean m_selection_block_mode; // FIXMEegmont move it into a 4th value in vte_selection_type?
enum vte_selection_type m_selection_type; enum vte_selection_type m_selection_type;
vte::view::coords m_selection_origin, m_selection_last; vte::grid::halfcoords m_selection_origin, m_selection_last;
VteVisualPosition m_selection_start, m_selection_end; vte::grid::span m_selection_resolved;
/* Clipboard data information. */ /* Clipboard data information. */
// FIXMEchpe check if this can make m_has_selection obsolete!
bool m_selection_owned[LAST_VTE_SELECTION]; bool m_selection_owned[LAST_VTE_SELECTION];
VteFormat m_selection_format[LAST_VTE_SELECTION]; VteFormat m_selection_format[LAST_VTE_SELECTION];
bool m_changing_selection; bool m_changing_selection;
GString *m_selection[LAST_VTE_SELECTION]; GString *m_selection[LAST_VTE_SELECTION]; // FIXMEegmont rename this so that m_selection_resolved can become m_selection?
GtkClipboard *m_clipboard[LAST_VTE_SELECTION]; GtkClipboard *m_clipboard[LAST_VTE_SELECTION];
ClipboardTextRequestGtk<Terminal> m_paste_request; ClipboardTextRequestGtk<Terminal> m_paste_request;
...@@ -675,8 +672,8 @@ public: ...@@ -675,8 +672,8 @@ public:
void invalidate_rows(vte::grid::row_t row_start, void invalidate_rows(vte::grid::row_t row_start,
vte::grid::row_t row_end /* inclusive */); vte::grid::row_t row_end /* inclusive */);
void invalidate(vte::grid::span const& s); void invalidate(vte::grid::span const& s);
void invalidate_symmetrical_difference(vte::grid::span const& a, vte::grid::span const& b, bool block);
void invalidate_match_span(); void invalidate_match_span();
void invalidate_selection();
void invalidate_all(); void invalidate_all();
void reset_update_rects(); void reset_update_rects();
...@@ -726,6 +723,7 @@ public: ...@@ -726,6 +723,7 @@ public:
vte::view::coords view_coords_from_grid_coords(vte::grid::coords const& rowcol) const; vte::view::coords view_coords_from_grid_coords(vte::grid::coords const& rowcol) const;
vte::grid::coords grid_coords_from_view_coords(vte::view::coords const& pos) const; vte::grid::coords grid_coords_from_view_coords(vte::view::coords const& pos) const;
vte::grid::halfcoords selection_grid_halfcoords_from_view_coords(vte::view::coords const& pos) const;
bool view_coords_visible(vte::view::coords const& pos) const; bool view_coords_visible(vte::view::coords const& pos) const;
bool grid_coords_visible(vte::grid::coords const& rowcol) const; bool grid_coords_visible(vte::grid::coords const& rowcol) const;
...@@ -944,20 +942,17 @@ public: ...@@ -944,20 +942,17 @@ public:
GString* attributes_to_html(GString* text_string, GString* attributes_to_html(GString* text_string,
GArray* attrs); GArray* attrs);
void start_selection(long x, void start_selection(vte::view::coords const& pos,
long y, enum vte_selection_type type);
enum vte_selection_type selection_type);
bool maybe_end_selection(); bool maybe_end_selection();
void extend_selection_expand();
void extend_selection(long x,
long y,
bool always_grow,
bool force);
void select_all(); void select_all();
void deselect_all(); void deselect_all();
vte::grid::coords resolve_selection_endpoint(vte::grid::halfcoords const& rowcolhalf, bool after) const;
void resolve_selection();
void selection_maybe_swap_endpoints(vte::view::coords const& pos);
void modify_selection(vte::view::coords const& pos);
bool cell_is_selected(vte::grid::column_t col, bool cell_is_selected(vte::grid::column_t col,
vte::grid::row_t) const; vte::grid::row_t) const;
......
...@@ -23,10 +23,8 @@ ...@@ -23,10 +23,8 @@
#include <type_traits> #include <type_traits>
static_assert(std::is_pod<vte::grid::coords>::value, "vte::grid::coords not POD");
static_assert(sizeof(vte::grid::coords) == 2 * sizeof(long), "vte::grid::coords size wrong"); static_assert(sizeof(vte::grid::coords) == 2 * sizeof(long), "vte::grid::coords size wrong");
static_assert(std::is_pod<vte::grid::span>::value, "vte::grid::span not POD");
static_assert(sizeof(vte::grid::span) == 4 * sizeof(long), "vte::grid::span size wrong"); static_assert(sizeof(vte::grid::span) == 4 * sizeof(long), "vte::grid::span size wrong");
static_assert(std::is_pod<vte::view::coords>::value, "vte::view::coords not POD"); static_assert(std::is_pod<vte::view::coords>::value, "vte::view::coords not POD");
...@@ -104,6 +102,14 @@ vte::grid::coords::to_string() const ...@@ -104,6 +102,14 @@ vte::grid::coords::to_string() const
return buf; return buf;
} }
char const*
vte::grid::halfcoords::to_string() const
{
char *buf = debug_get_buf();
g_snprintf(buf, DEBUG_STRING_SIZE, "halfgrid[%ld,%ld%c]", row(), halfcolumn().column(), halfcolumn().half() ? 'R' : 'L');
return buf;
}
char const* char const*
vte::grid::span::to_string() const vte::grid::span::to_string() const
{ {
...@@ -203,6 +209,61 @@ test_grid_coords (void) ...@@ -203,6 +209,61 @@ test_grid_coords (void)
#endif #endif
} }
static void
test_grid_halfcoords (void)
{
/* Default constructor */
halfcoords p1;
/* Construction and assignment */
halfcoords p2(16, halfcolumn_t(32, 1));
g_assert_cmpint(p2.row(), ==, 16);
g_assert_cmpint(p2.halfcolumn().column(), ==, 32);
g_assert_cmpint(p2.halfcolumn().half(), ==, 1);
/* Comparision operators */
halfcoords a (10, halfcolumn_t(20, 1));
halfcoords a2(10, halfcolumn_t(20, 1));
halfcoords b (10, halfcolumn_t(21, 0));
halfcoords c (10, halfcolumn_t(21, 1));
halfcoords d (10, halfcolumn_t(22, 0));
halfcoords e (11, halfcolumn_t( 5, 0));
g_assert_true (a <= a2);
g_assert_false(a < a2);
g_assert_false(a > a2);
g_assert_true (a >= a2);
g_assert_true (a <= b);
g_assert_true (a < b);
g_assert_false(a > b);
g_assert_false(a >= b);
g_assert_true (b <= c);
g_assert_true (b < c);
g_assert_false(b > c);
g_assert_false(b >= c);
g_assert_true (c <= d);
g_assert_true (c < d);
g_assert_false(c > d);
g_assert_false(c >= d);
g_assert_true (d <= e);
g_assert_true (d < e);
g_assert_false(d > e);
g_assert_false(d >= e);
#ifdef VTE_DEBUG
/* to_string() */
g_assert_cmpstr(halfcoords(16, 32, 0).to_string(), ==, "halfgrid[16,32L]");
g_assert_cmpstr(halfcoords(16, 32, 1).to_string(), ==, "halfgrid[16,32R]");
#endif
}
static void static void
test_grid_span (void) test_grid_span (void)
{ {
...@@ -294,6 +355,13 @@ test_grid_span (void) ...@@ -294,6 +355,13 @@ test_grid_span (void)
g_assert_false(s8.box_contains(coords(33, 24))); g_assert_false(s8.box_contains(coords(33, 24)));
g_assert_false(s8.box_contains(coords(33, 42))); g_assert_false(s8.box_contains(coords(33, 42)));
/* last_row */
span s9(16, 16, 32, 0);
g_assert_cmpint(s9.last_row(), ==, 31);
span s10(16, 16, 32, 1);
g_assert_cmpint(s10.last_row(), ==, 32);
#ifdef VTE_DEBUG #ifdef VTE_DEBUG
/* to_string() */ /* to_string() */
g_assert_cmpstr(vte::grid::span(17, 42, 18, 3).to_string(), ==, "grid[(17,42), (18,3))"); g_assert_cmpstr(vte::grid::span(17, 42, 18, 3).to_string(), ==, "grid[(17,42), (18,3))");
...@@ -376,6 +444,7 @@ main(int argc, char *argv[]) ...@@ -376,6 +444,7 @@ main(int argc, char *argv[])
g_test_init (&argc, &argv, nullptr); g_test_init (&argc, &argv, nullptr);
g_test_add_func("/vte/c++/grid/coords", test_grid_coords); g_test_add_func("/vte/c++/grid/coords", test_grid_coords);
g_test_add_func("/vte/c++/grid/halfcoords", test_grid_halfcoords);
g_test_add_func("/vte/c++/grid/span", test_grid_span); g_test_add_func("/vte/c++/grid/span", test_grid_span);
g_test_add_func("/vte/c++/color/rgb", test_color_rgb); g_test_add_func("/vte/c++/color/rgb", test_color_rgb);
g_test_add_func("/vte/c++/view/coords", test_view_coords); g_test_add_func("/vte/c++/view/coords", test_view_coords);
......
...@@ -36,31 +36,53 @@ namespace grid { ...@@ -36,31 +36,53 @@ namespace grid {
typedef long row_t; typedef long row_t;
typedef long column_t; typedef long column_t;
typedef int half_t;
struct coords { struct coords : public std::pair<row_t, column_t> {
public: public:
using base_type = std::pair<row_t, column_t>;
coords() = default; coords() = default;
coords(row_t r, column_t c) : m_row(r), m_column(c) { } coords(row_t r, column_t c) : base_type{r, c} { }
inline void set_row(row_t r) { first = r; }
inline void set_column(column_t c) { second = c; }
inline void set_row(row_t r) { m_row = r; } inline row_t row() const { return first; }
inline void set_column(column_t c) { m_column = c; } inline column_t column() const { return second; }
inline row_t row() const { return m_row; } IFDEF_DEBUG(char const* to_string() const);
inline column_t column() const { return m_column; } };
inline bool operator == (coords const& rhs) const { return m_row == rhs.m_row && m_column == rhs.m_column; } struct halfcolumn_t : public std::pair<column_t, half_t> {
inline bool operator != (coords const& rhs) const { return m_row != rhs.m_row || m_column != rhs.m_column; } public:
using base_type = std::pair<column_t, half_t>;
inline bool operator < (coords const& rhs) const { return m_row < rhs.m_row || (m_row == rhs.m_row && m_column < rhs.m_column); } halfcolumn_t() = default;
inline bool operator <= (coords const& rhs) const { return m_row < rhs.m_row || (m_row == rhs.m_row && m_column <= rhs.m_column); } halfcolumn_t(column_t c, half_t h) : base_type{c, h} { }
inline bool operator > (coords const& rhs) const { return m_row > rhs.m_row || (m_row == rhs.m_row && m_column > rhs.m_column); }
inline bool operator >= (coords const& rhs) const { return m_row > rhs.m_row || (m_row == rhs.m_row && m_column >= rhs.m_column); }
IFDEF_DEBUG(char const* to_string() const); inline void set_column(column_t c) { first = c; }
inline void set_half(half_t h) { second = h; }
private: inline column_t column() const { return first; }
row_t m_row; inline half_t half() const { return second; }
column_t m_column; };
struct halfcoords : public std::pair<row_t, halfcolumn_t> {
public:
using base_type = std::pair<row_t, halfcolumn_t>;
halfcoords() = default;
halfcoords(row_t r, halfcolumn_t hc) : base_type{r, hc} { }
halfcoords(row_t r, column_t c, half_t h) : base_type{r, halfcolumn_t(c, h)} { }
inline void set_row(row_t r) { first = r; }
inline void set_halfcolumn(halfcolumn_t hc) { second = hc; }
inline row_t row() const { return first; }
inline halfcolumn_t halfcolumn() const { return second; }
IFDEF_DEBUG(char const* to_string() const);
}; };
/* end is exclusive (or: start and end point to boundaries between cells) */ /* end is exclusive (or: start and end point to boundaries between cells) */
...@@ -81,6 +103,8 @@ namespace grid { ...@@ -81,6 +103,8 @@ namespace grid {
inline coords const& end() const { return m_end; } inline coords const& end() const { return m_end; }
inline row_t start_row() const { return m_start.row(); } inline row_t start_row() const { return m_start.row(); }
inline row_t end_row() const { return m_end.row(); } inline row_t end_row() const { return m_end.row(); }
/* Get the last row that actually contains characters belonging to this span. */
inline row_t last_row() const { return m_end.column() > 0 ? m_end.row() : m_end.row() - 1; }
inline column_t start_column() const { return m_start.column(); } inline column_t start_column() const { return m_start.column(); }
inline column_t end_column() const { return m_end.column(); } inline column_t end_column() const { return m_end.column(); }
...@@ -89,6 +113,7 @@ namespace grid { ...@@ -89,6 +113,7 @@ namespace grid {
inline explicit operator bool() const { return !empty(); } inline explicit operator bool() const { return !empty(); }
inline bool contains(coords const& p) const { return m_start <= p && p < m_end; } inline bool contains(coords const& p) const { return m_start <= p && p < m_end; }
// FIXME make "block" a member of the span? Or subclasses for regular and block spans?
inline bool box_contains(coords const& p) const { return m_start.row() <= p.row() && p.row() <= m_end.row() && inline bool box_contains(coords const& p) const { return m_start.row() <= p.row() && p.row() <= m_end.row() &&
m_start.column() <= p.column() && p.column() < m_end.column(); } m_start.column() <= p.column() && p.column() < m_end.column(); }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment