Skip to content

Commit b780ca0

Browse files
committed
[ui] horizontal scroll should work on columns
Related to #1685
1 parent 26fe313 commit b780ca0

14 files changed

Lines changed: 272 additions & 12 deletions

src/db_sub_source.cc

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,27 @@ db_label_source::text_attrs_for_line(textview_curses& tc,
321321
}
322322
}
323323

324+
void
325+
db_label_source::text_horiz_columns(textview_curses& tc,
326+
vis_line_t start_row,
327+
vis_line_t end_row,
328+
std::set<int>& columns_out)
329+
{
330+
int cell_start = 0;
331+
for (size_t lpc = 0; lpc < this->dls_headers.size(); lpc++) {
332+
if (lpc == this->dls_row_style_column
333+
&& !this->dls_row_styles_have_errors)
334+
{
335+
continue;
336+
}
337+
if (this->dls_headers[lpc].hm_hidden) {
338+
continue;
339+
}
340+
columns_out.insert(cell_start);
341+
cell_start += this->dls_cell_width[lpc] + 1;
342+
}
343+
}
344+
324345
void
325346
db_label_source::set_col_as_graphable(int lpc)
326347
{
@@ -772,7 +793,7 @@ db_label_source::list_input_handle_key(listview_curses& lv, const ncinput& ch)
772793
}
773794
}
774795

775-
return false;
796+
return text_sub_source::list_input_handle_key(lv, ch);
776797
}
777798

778799
std::optional<json_string>

src/db_sub_source.hh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
class db_label_source
5353
: public text_sub_source
5454
, public text_time_translator
55-
, public list_input_delegate
5655
, public text_delegate
5756
, public text_detail_provider {
5857
public:
@@ -88,6 +87,11 @@ public:
8887
int row,
8988
string_attrs_t& sa) override;
9089

90+
void text_horiz_columns(textview_curses& tc,
91+
vis_line_t start_row,
92+
vis_line_t end_row,
93+
std::set<int>& columns_out) override;
94+
9195
void push_header(const std::string& colstr, int type);
9296

9397
void set_col_as_graphable(int lpc);

src/files_sub_source.hh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646

4747
class files_sub_source
4848
: public text_sub_source
49-
, public list_input_delegate
5049
, public text_delegate {
5150
public:
5251
bool empty() const override { return false; }

src/filter_sub_source.hh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ class textinput_curses;
4747

4848
class filter_sub_source
4949
: public text_sub_source
50-
, public list_input_delegate
5150
, public text_delegate {
5251
public:
5352
explicit filter_sub_source(std::shared_ptr<textinput_curses> editor);

src/listview_curses.hh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,13 @@ public:
223223
return *this;
224224
}
225225

226+
listview_curses& remove_input_delegate(list_input_delegate& lid)
227+
{
228+
this->lv_input_delegates.remove(&lid);
229+
230+
return *this;
231+
}
232+
226233
/**
227234
* @param va The action to invoke when the view is scrolled.
228235
* @todo Allow multiple observers.

src/logfile_sub_source.cc

Lines changed: 122 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1138,6 +1138,127 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
11381138
value_out = std::move(this->lss_token_al.al_attrs);
11391139
}
11401140

1141+
void
1142+
logfile_sub_source::text_horiz_columns(textview_curses& tc,
1143+
vis_line_t start_row,
1144+
vis_line_t end_row,
1145+
std::set<int>& columns_out)
1146+
{
1147+
if (this->lss_indexing_in_progress) {
1148+
return;
1149+
}
1150+
1151+
auto count = vis_line_t((int) this->text_line_count());
1152+
auto begin = std::max(0_vl, start_row);
1153+
auto last = std::min(count, end_row);
1154+
if (last <= begin) {
1155+
return;
1156+
}
1157+
auto win = this->window_at(begin, last);
1158+
std::map<intern_string_t, int> field2left;
1159+
std::optional<int> ts_left_opt;
1160+
std::optional<int> level_left_opt;
1161+
std::optional<int> body_left_opt;
1162+
for (const auto& msg : *win) {
1163+
const auto& values = msg.get_values();
1164+
auto line_sf = values.lvv_sbr.to_string_fragment();
1165+
std::optional<line_range> ts_range;
1166+
std::optional<line_range> level_range;
1167+
auto time_col_size = this->lss_time_column_size;
1168+
if (time_col_size > 0) {
1169+
time_col_size -= 1;
1170+
}
1171+
for (const auto& sa : msg.get_attrs()) {
1172+
if (sa.sa_type != &L_TIMESTAMP && sa.sa_type != &L_LEVEL) {
1173+
continue;
1174+
}
1175+
if (!sa.sa_range.is_valid()) {
1176+
continue;
1177+
}
1178+
if (this->lss_line_context == line_context_t::time_column) {
1179+
if (sa.sa_type == &L_TIMESTAMP) {
1180+
ts_range = sa.sa_range;
1181+
} else {
1182+
level_range = sa.sa_range;
1183+
}
1184+
} else {
1185+
auto sa_left
1186+
= (int) line_sf.byte_to_column_index(sa.sa_range.lr_start);
1187+
if (sa.sa_type == &L_TIMESTAMP) {
1188+
if (!ts_left_opt || sa_left < ts_left_opt.value()) {
1189+
ts_left_opt = sa_left;
1190+
}
1191+
} else {
1192+
if (!level_left_opt || sa_left < level_left_opt.value()) {
1193+
level_left_opt = sa_left;
1194+
}
1195+
}
1196+
}
1197+
}
1198+
for (const auto& sa : msg.get_attrs()) {
1199+
if (sa.sa_type != &SA_BODY) {
1200+
continue;
1201+
}
1202+
if (!sa.sa_range.is_valid()) {
1203+
continue;
1204+
}
1205+
auto curr_range = sa.sa_range;
1206+
if (ts_range && ts_range.value() < sa.sa_range) {
1207+
curr_range.lr_start -= ts_range->length();
1208+
}
1209+
if (level_range && level_range.value() < sa.sa_range) {
1210+
curr_range.lr_start -= level_range->length();
1211+
}
1212+
curr_range.lr_start += time_col_size;
1213+
auto body_left
1214+
= (int) line_sf.byte_to_column_index(curr_range.lr_start);
1215+
if (!body_left_opt || body_left < body_left_opt.value()) {
1216+
body_left_opt = body_left;
1217+
}
1218+
}
1219+
for (const auto& lv : values.lvv_values) {
1220+
if (!lv.lv_origin.is_valid() || lv.lv_origin.lr_start < 0) {
1221+
continue;
1222+
}
1223+
if (lv.lv_meta.lvm_column.is<logline_value_meta::internal_column>())
1224+
{
1225+
continue;
1226+
}
1227+
auto curr_range = lv.lv_origin;
1228+
if (ts_range && ts_range.value() < lv.lv_origin) {
1229+
curr_range.lr_start -= ts_range->length();
1230+
}
1231+
if (level_range && level_range.value() < lv.lv_origin) {
1232+
curr_range.lr_start -= level_range->length();
1233+
}
1234+
curr_range.lr_start += time_col_size;
1235+
1236+
auto left_for_value
1237+
= (int) line_sf.byte_to_column_index(curr_range.lr_start);
1238+
auto iter = field2left.find(lv.lv_meta.lvm_name);
1239+
if (iter == field2left.end()) {
1240+
field2left.emplace(lv.lv_meta.lvm_name, left_for_value);
1241+
} else if (left_for_value < iter->second) {
1242+
iter->second = left_for_value;
1243+
}
1244+
}
1245+
}
1246+
1247+
for (const auto& [name, left] : field2left) {
1248+
log_debug("inserting %d for %s", left, name.c_str());
1249+
columns_out.insert(left);
1250+
}
1251+
if (body_left_opt) {
1252+
columns_out.insert(body_left_opt.value());
1253+
}
1254+
if (ts_left_opt) {
1255+
columns_out.insert(ts_left_opt.value());
1256+
}
1257+
if (level_left_opt) {
1258+
columns_out.insert(level_left_opt.value());
1259+
}
1260+
}
1261+
11411262
struct logline_cmp {
11421263
explicit logline_cmp(logfile_sub_source& lc) : llss_controller(lc) {}
11431264

@@ -2172,7 +2293,7 @@ logfile_sub_source::list_input_handle_key(listview_curses& lv,
21722293
}
21732294
break;
21742295
}
2175-
return false;
2296+
return text_sub_source::list_input_handle_key(lv, ch);
21762297
}
21772298

21782299
std::optional<

src/logfile_sub_source.hh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ class logfile_sub_source
139139
: public text_sub_source
140140
, public text_time_translator
141141
, public text_accel_source
142-
, public list_input_delegate
143142
, public text_anchors
144143
, public text_delegate
145144
, public text_detail_provider
@@ -206,6 +205,11 @@ public:
206205
int row,
207206
string_attrs_t& value_out);
208207

208+
void text_horiz_columns(textview_curses& tc,
209+
vis_line_t start_row,
210+
vis_line_t end_row,
211+
std::set<int>& columns_out);
212+
209213
size_t text_size_for_line(textview_curses& tc, int row, line_flags_t flags);
210214

211215
void text_mark(const bookmark_type_t* bm, vis_line_t line, bool added);

src/spectro_source.hh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,6 @@ class spectrogram_source
156156
: public text_sub_source
157157
, public text_time_translator
158158
, public list_overlay_source
159-
, public list_input_delegate
160159
, public text_delegate {
161160
public:
162161
~spectrogram_source() override = default;

src/textview_curses.cc

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
*/
2929

3030
#include <algorithm>
31+
#include <climits>
3132
#include <string>
3233
#include <vector>
3334

@@ -1295,16 +1296,98 @@ textview_curses&
12951296
textview_curses::set_sub_source(text_sub_source* src)
12961297
{
12971298
if (this->tc_sub_source != src) {
1299+
if (this->tc_sub_source != nullptr) {
1300+
this->remove_input_delegate(*this->tc_sub_source);
1301+
}
12981302
this->tc_bookmarks.clear();
12991303
this->tc_sub_source = src;
13001304
if (src) {
13011305
src->register_view(this);
1306+
this->add_input_delegate(*src);
13021307
}
13031308
this->reload_data();
13041309
}
13051310
return *this;
13061311
}
13071312

1313+
bool
1314+
text_sub_source::list_input_handle_key(listview_curses& lv, const ncinput& ch)
1315+
{
1316+
// Only intercept plain Left/Right; let everything else fall through to
1317+
// listview_curses::handle_key so the existing 10-char / half-width /
1318+
// line-nav behavior applies unchanged.
1319+
if (ncinput_shift_p(&ch) || this->tss_view == nullptr) {
1320+
return false;
1321+
}
1322+
int direction;
1323+
switch (ch.eff_text[0]) {
1324+
case NCKEY_LEFT:
1325+
direction = -1;
1326+
break;
1327+
case NCKEY_RIGHT:
1328+
direction = 1;
1329+
break;
1330+
default:
1331+
return false;
1332+
}
1333+
1334+
auto& tc = *this->tss_view;
1335+
auto [height, width] = tc.get_dimensions();
1336+
auto top = tc.get_top();
1337+
auto count = vis_line_t((int) this->text_line_count());
1338+
auto start = std::max(0_vl, top - height);
1339+
auto end = std::min(count, top + 2_vl * height);
1340+
1341+
std::set<int> cols;
1342+
this->text_horiz_columns(tc, start, end, cols);
1343+
if (cols.empty()) {
1344+
return false;
1345+
}
1346+
cols.insert(0);
1347+
1348+
// Step by a viewport width, but don't cross a column boundary in the
1349+
// press direction. When the current column is wider than the viewport,
1350+
// this lets repeated presses scroll through its content; when a
1351+
// boundary is closer than a viewport away, the press snaps to it.
1352+
int cur = tc.get_left();
1353+
int target;
1354+
if (direction > 0) {
1355+
while (true) {
1356+
auto it = cols.upper_bound(cur);
1357+
int next_boundary = (it != cols.end()) ? *it : INT_MAX;
1358+
if (next_boundary - cur <= 2) {
1359+
cur = next_boundary;
1360+
continue;
1361+
}
1362+
target = std::min(cur + (int) width / 2, next_boundary);
1363+
break;
1364+
}
1365+
} else {
1366+
while (true) {
1367+
auto it = cols.lower_bound(cur);
1368+
int prev_boundary = (it != cols.begin()) ? *std::prev(it) : 0;
1369+
if (prev_boundary < cur && cur - prev_boundary <= 2) {
1370+
cur = prev_boundary;
1371+
continue;
1372+
}
1373+
target = std::max(cur - (int) width / 2, prev_boundary);
1374+
break;
1375+
}
1376+
if (target < 0) {
1377+
target = 0;
1378+
}
1379+
}
1380+
1381+
if (target == cur) {
1382+
// No movement possible at this edge — fall through so the
1383+
// listview's default Left/Right behavior (and its right-edge
1384+
// chime in set_left) applies.
1385+
return false;
1386+
}
1387+
tc.set_left(target);
1388+
return true;
1389+
}
1390+
13081391
std::optional<line_info>
13091392
textview_curses::grep_value_for_line(vis_line_t line, std::string& value_out)
13101393
{

0 commit comments

Comments
 (0)