22
22
23
23
from renku .command .view_model .text_canvas import Point
24
24
from renku .core import errors
25
+ from renku .core .util import communication
25
26
from renku .domain_model .provenance .activity import Activity
26
27
27
28
@@ -59,13 +60,14 @@ def __init__(
59
60
self .current_layer = 0
60
61
self .layer_position = 0
61
62
self .max_layer = len (navigation_data ) - 1
62
- self .y_pos = 0
63
- self .x_pos = 0
63
+ self .y_pos : int = 0
64
+ self .x_pos : int = 0
64
65
self .color_cache : Dict [str , int ] = {}
65
66
self .activity_overlay : Optional [curses ._CursesWindow ] = None
66
67
self .help_overlay : Optional [curses ._CursesWindow ] = None
67
68
self ._select_activity ()
68
69
self .use_color = use_color
70
+ self .free_move : bool = False
69
71
70
72
def _init_curses (self , screen ):
71
73
"""Initialize curses screen for interactive mode."""
@@ -88,7 +90,17 @@ def _init_curses(self, screen):
88
90
text_data_lines = self .text_data .splitlines ()
89
91
90
92
self .content_max_x = max (len (line ) for line in text_data_lines )
91
- self .content_max_y = len (self .text_data )
93
+ self .content_max_y = len (text_data_lines )
94
+
95
+ int16_max = 32767
96
+ if self .content_max_y > int16_max or self .content_max_x > int16_max :
97
+ communication .warn (
98
+ f"Graph is too large for interactive visualization, cropping to { int16_max } lines/columns."
99
+ )
100
+ self .content_max_x = min (self .content_max_x , int16_max )
101
+ self .content_max_y = min (self .content_max_y , int16_max )
102
+ text_data_lines = [line [: self .content_max_x ] for line in text_data_lines [self .content_max_y ]]
103
+
92
104
self .content_pad = curses .newpad (self .content_max_y , self .content_max_x )
93
105
for i , l in enumerate (text_data_lines ):
94
106
self ._addstr_with_color_codes (self .content_pad , i , 0 , l )
@@ -281,6 +293,7 @@ def _update_help_overlay(self, screen):
281
293
content = (
282
294
"Navigate using arrow keys\n "
283
295
"Press <enter> to show activity details\n "
296
+ "Press <f> to toggle free arrow movement\n "
284
297
"Press <h> to show/hide this help\n "
285
298
"Press <q> to exit\n "
286
299
)
@@ -290,7 +303,7 @@ def _update_help_overlay(self, screen):
290
303
del self .help_overlay
291
304
self .help_overlay = None
292
305
293
- def _move_viewscreen (self ):
306
+ def _move_viewscreen_to_activity (self ):
294
307
"""Move viewscreen to include selected activity."""
295
308
if self .activity_start .x - 1 < self .x_pos :
296
309
self .x_pos = max (self .activity_start .x - 1 , 0 )
@@ -314,25 +327,40 @@ def _loop(self, screen):
314
327
315
328
# handle keypress
316
329
if input_char == curses .KEY_DOWN or chr (input_char ) == "k" :
317
- self ._change_layer (1 )
330
+ if self .free_move :
331
+ self .y_pos = min (self .y_pos + 1 , self .content_max_y - self .rows - 1 )
332
+ else :
333
+ self ._change_layer (1 )
318
334
elif input_char == curses .KEY_UP or chr (input_char ) == "i" :
319
- self ._change_layer (- 1 )
335
+ if self .free_move :
336
+ self .y_pos = max (self .y_pos - 1 , 0 )
337
+ else :
338
+ self ._change_layer (- 1 )
320
339
elif input_char == curses .KEY_RIGHT or chr (input_char ) == "l" :
321
- self ._change_layer_position (1 )
340
+ if self .free_move :
341
+ self .x_pos = min (self .x_pos + 1 , self .content_max_x - self .cols - 1 )
342
+ else :
343
+ self ._change_layer_position (1 )
322
344
elif input_char == curses .KEY_LEFT or chr (input_char ) == "j" :
323
- self ._change_layer_position (- 1 )
345
+ if self .free_move :
346
+ self .x_pos = max (self .x_pos - 1 , 0 )
347
+ else :
348
+ self ._change_layer_position (- 1 )
324
349
elif input_char == curses .KEY_ENTER or input_char == 10 or input_char == 13 :
325
350
self ._update_activity_overlay (screen )
326
351
elif chr (input_char ) == "h" :
327
352
self ._update_help_overlay (screen )
353
+ elif chr (input_char ) == "f" :
354
+ self .free_move = not self .free_move
328
355
elif input_char < 256 and chr (input_char ) == "q" :
329
356
running = False
330
357
331
358
self ._unblink_text (self .content_pad , self .activity_start , self .activity_end , bold = True )
332
359
self ._select_activity ()
333
360
self ._blink_text (self .content_pad , self .activity_start , self .activity_end , bold = True )
334
361
335
- self ._move_viewscreen ()
362
+ if not self .free_move :
363
+ self ._move_viewscreen_to_activity ()
336
364
337
365
self ._refresh (screen )
338
366
0 commit comments