@@ -456,10 +456,20 @@ defmodule Owl.LiveScreen do
456
456
457
457
{ state , render_above_data , io_reply } = render_above ( state )
458
458
459
+ # -1, because we need to leave one row for the last new line (cursor)
460
+ rows_left = Owl.IO . rows ( state . device ) - 1
461
+
462
+ { state , render_added_blocks_data , rows_left } =
463
+ render_added_blocks ( state , terminal_width , rows_left )
464
+
459
465
{ state , render_updated_blocks_data } =
460
- rerender_updated_blocks ( state , render_above_data != [ ] , terminal_width )
466
+ rerender_updated_blocks ( state , render_above_data != [ ] , terminal_width , rows_left )
461
467
462
- { state , render_added_blocks_data } = render_added_blocks ( state , terminal_width )
468
+ state = % {
469
+ state
470
+ | blocks_to_add: [ ] ,
471
+ rendered_blocks: state . rendered_blocks ++ state . blocks_to_add
472
+ }
463
473
464
474
data =
465
475
[
@@ -482,7 +492,7 @@ defmodule Owl.LiveScreen do
482
492
% { state | block_states: % { } , notify_on_next_render: [ ] }
483
493
end
484
494
485
- defp get_content ( state , block_id , terminal_width ) do
495
+ defp get_content ( state , block_id , terminal_width , rows_left ) do
486
496
case Map . fetch ( state . block_states , block_id ) do
487
497
{ :ok , block_state } ->
488
498
block_content = state . render_functions [ block_id ] . ( block_state )
@@ -495,15 +505,71 @@ defmodule Owl.LiveScreen do
495
505
line -> Owl.Data . chunk_every ( line , terminal_width )
496
506
end )
497
507
498
- {
508
+ block_height = length ( lines )
509
+
510
+ max_height = max ( block_height , state . rendered_content_height [ block_id ] [ :max ] || 0 )
511
+ lines = lines ++ List . duplicate ( [ ] , max_height - block_height )
512
+ block_height = max_height
513
+ new_rows_left = max ( rows_left - block_height , 0 )
514
+
515
+ content_height_to_render = rows_left - new_rows_left
516
+
517
+ lines_to_render =
518
+ lines
519
+ |> Enum . reverse ( )
520
+ |> Enum . slice ( 0 , content_height_to_render )
521
+ |> Enum . reverse ( )
522
+
523
+ block_content =
499
524
lines
500
525
|> Enum . map ( & [ IO.ANSI . clear_line ( ) , & 1 ] )
501
- |> Owl.Data . unlines ( ) ,
502
- length ( lines )
526
+ |> Owl.Data . unlines ( )
527
+
528
+ content_to_render =
529
+ if content_height_to_render == block_height do
530
+ block_content
531
+ else
532
+ lines_to_render
533
+ |> Enum . map ( & [ IO.ANSI . clear_line ( ) , & 1 ] )
534
+ |> Owl.Data . unlines ( )
535
+ end
536
+
537
+ % {
538
+ rendered_new_state?: true ,
539
+ block_content: block_content ,
540
+ block_height: block_height ,
541
+ new_rows_left: new_rows_left ,
542
+ content_to_render: content_to_render ,
543
+ content_to_render_height: content_height_to_render
503
544
}
504
545
505
546
:error ->
506
- { state . content [ block_id ] , state . rendered_content_height [ block_id ] }
547
+ block_content = state . content [ block_id ]
548
+ block_height = state . rendered_content_height [ block_id ] . max
549
+
550
+ new_rows_left = max ( rows_left - block_height , 0 )
551
+ content_height_to_render = rows_left - new_rows_left
552
+
553
+ content_to_render =
554
+ if content_height_to_render == block_height do
555
+ block_content
556
+ else
557
+ block_content
558
+ |> Owl.Data . lines ( )
559
+ |> Enum . reverse ( )
560
+ |> Enum . slice ( 0 , content_height_to_render )
561
+ |> Enum . reverse ( )
562
+ |> Owl.Data . unlines ( )
563
+ end
564
+
565
+ % {
566
+ rendered_new_state?: false ,
567
+ block_content: block_content ,
568
+ block_height: block_height ,
569
+ new_rows_left: new_rows_left ,
570
+ content_to_render: content_to_render ,
571
+ content_to_render_height: content_height_to_render
572
+ }
507
573
end
508
574
end
509
575
@@ -512,7 +578,7 @@ defmodule Owl.LiveScreen do
512
578
defp render_above ( % { put_above_blocks: [ ] } = state ) , do: { state , [ ] , & noop / 0 }
513
579
514
580
defp render_above ( % { put_above_blocks: put_above_blocks } = state ) do
515
- blocks_height = Enum . sum ( Map . values ( state . rendered_content_height ) )
581
+ blocks_height = Enum . sum_by ( Map . values ( state . rendered_content_height ) , & & 1 . rendered )
516
582
data = Enum . reverse ( put_above_blocks )
517
583
518
584
cursor_up =
@@ -548,101 +614,153 @@ defmodule Owl.LiveScreen do
548
614
List . duplicate ( [ IO.ANSI . cursor_up ( 1 ) , IO.ANSI . clear_line ( ) ] , n )
549
615
end
550
616
551
- defp rerender_updated_blocks ( state , rendered_above? , terminal_width ) do
617
+ defp rerender_updated_blocks ( state , rendered_above? , terminal_width , rows_left ) do
552
618
blocks_to_replace = Map . keys ( state . block_states ) -- state . blocks_to_add
553
619
554
620
if not rendered_above? and Enum . empty? ( blocks_to_replace ) do
555
621
{ state , [ ] }
556
622
else
557
- { content_blocks , % { total_height: total_height , state: state , next_offset: return_to_end } } =
623
+ { content_blocks , % { state: state } } =
558
624
state . rendered_blocks
559
- |> Enum . flat_map_reduce (
560
- % { total_height: 0 , next_offset: 0 , force_rerender?: rendered_above? , state: state } ,
561
- fn block_id ,
625
+ |> Enum . reverse ( )
626
+ |> Enum . map_reduce (
627
+ % { state: state , rows_left: rows_left } ,
628
+ fn block_id , % { state: state , rows_left: rows_left } ->
629
+ % {
630
+ rendered_new_state?: rendered_new_state? ,
631
+ block_content: block_content ,
632
+ block_height: block_height ,
633
+ new_rows_left: new_rows_left ,
634
+ content_to_render: content_to_render ,
635
+ content_to_render_height: content_to_render_height
636
+ } = get_content ( state , block_id , terminal_width , rows_left )
637
+
638
+ { % {
639
+ height_diff:
640
+ content_to_render_height - state . rendered_content_height [ block_id ] . rendered ,
641
+ content: content_to_render ,
642
+ height: content_to_render_height ,
643
+ rendered_new_state?: rendered_new_state? ,
644
+ visible?: rows_left > 0
645
+ } ,
562
646
% {
563
- total_height: total_height ,
564
- next_offset: next_offset ,
565
- state: state ,
566
- force_rerender?: force_rerender?
567
- } ->
568
- if force_rerender? or block_id in blocks_to_replace do
569
- { block_content , height } = get_content ( state , block_id , terminal_width )
570
-
571
- max_height = max ( height , state . rendered_content_height [ block_id ] )
572
-
573
- block_content = [
574
- block_content ,
575
- List . duplicate ( [ "\n " , IO.ANSI . clear_line ( ) ] , max_height - height )
576
- ]
577
-
578
- { [ % { offset: next_offset , content: block_content } ] ,
579
- % {
580
- total_height: total_height + state . rendered_content_height [ block_id ] ,
581
- next_offset: 0 ,
582
- force_rerender?:
583
- force_rerender? || height > state . rendered_content_height [ block_id ] ,
584
- state: % {
585
- state
586
- | rendered_content_height:
587
- Map . put ( state . rendered_content_height , block_id , max_height ) ,
588
- content: Map . put ( state . content , block_id , block_content )
589
- }
590
- } }
591
- else
592
- height = state . rendered_content_height [ block_id ]
593
-
594
- { [ ] ,
595
- % {
596
- total_height: total_height + height ,
597
- next_offset: next_offset + height ,
598
- state: state ,
599
- force_rerender?: force_rerender?
600
- } }
647
+ rows_left: new_rows_left ,
648
+ state: % {
649
+ state
650
+ | rendered_content_height:
651
+ Map . put ( state . rendered_content_height , block_id , % {
652
+ rendered: content_to_render_height ,
653
+ max: block_height
654
+ } ) ,
655
+ content: Map . put ( state . content , block_id , block_content )
656
+ }
657
+ } }
658
+ end
659
+ )
660
+
661
+ content_blocks = Enum . reverse ( content_blocks )
662
+
663
+ { content_blocks , % { total_height: total_height , next_offset: return_to_end } } =
664
+ Enum . flat_map_reduce (
665
+ content_blocks ,
666
+ % {
667
+ content_above_increased_height?: rendered_above? ,
668
+ next_offset: 0 ,
669
+ total_height: 0
670
+ } ,
671
+ fn block , acc ->
672
+ cond do
673
+ not block . visible? ->
674
+ { [ ] , acc }
675
+
676
+ acc . content_above_increased_height? || block . height_diff > 0 ->
677
+ { [ Map . put ( block , :offset , acc . next_offset ) ] ,
678
+ % {
679
+ acc
680
+ | content_above_increased_height?: true ,
681
+ next_offset: 0 ,
682
+ total_height: block . height + acc . total_height - block . height_diff
683
+ } }
684
+
685
+ block . rendered_new_state? ->
686
+ { [ Map . put ( block , :offset , acc . next_offset ) ] ,
687
+ % {
688
+ acc
689
+ | next_offset: 0 ,
690
+ # - block.height_diff
691
+ total_height: block . height + acc . total_height
692
+ } }
693
+
694
+ acc . next_offset == 0 and acc . total_height == 0 ->
695
+ { [ ] , acc }
696
+
697
+ true ->
698
+ { [ ] ,
699
+ % {
700
+ acc
701
+ | next_offset: acc . next_offset + block . height ,
702
+ total_height: block . height + acc . total_height
703
+ } }
601
704
end
602
705
end
603
706
)
604
707
605
- if content_blocks == [ ] do
606
- { state , [ ] }
607
- else
608
- data = [
609
- if ( rendered_above? or total_height == 0 , do: [ ] , else: IO.ANSI . cursor_up ( total_height ) ) ,
610
- content_blocks
611
- |> Enum . map ( fn
612
- % { offset: 0 , content: content } -> content
613
- % { offset: offset , content: content } -> [ IO.ANSI . cursor_down ( offset ) , content ]
614
- end )
615
- |> Owl.Data . unlines ( ) ,
616
- if ( return_to_end == 0 , do: [ ] , else: IO.ANSI . cursor_down ( return_to_end ) )
617
- ]
708
+ data =
709
+ if content_blocks == [ ] do
710
+ [ ]
711
+ else
712
+ [
713
+ if ( rendered_above? , do: [ ] , else: IO.ANSI . cursor_up ( total_height ) ) ,
714
+ content_blocks
715
+ |> Enum . map ( fn
716
+ % { offset: 0 , content: content } -> content
717
+ % { offset: offset , content: content } -> [ IO.ANSI . cursor_down ( offset ) , content ]
718
+ end )
719
+ |> Owl.Data . unlines ( ) ,
720
+ if ( return_to_end == 0 , do: [ ] , else: IO.ANSI . cursor_down ( return_to_end ) )
721
+ ]
722
+ end
618
723
619
- { state , data }
620
- end
724
+ { state , data }
621
725
end
622
726
end
623
727
624
- defp render_added_blocks ( % { blocks_to_add: [ ] } = state , _terminal_width ) , do: { state , [ ] }
625
-
626
- defp render_added_blocks ( state , terminal_width ) do
627
- { content_blocks , state } =
628
- Enum . map_reduce ( state . blocks_to_add , state , fn block_id , state ->
629
- { block_content , height } = get_content ( state , block_id , terminal_width )
728
+ defp render_added_blocks ( % { blocks_to_add: [ ] } = state , _terminal_width , rows_left ) ,
729
+ do: { state , [ ] , rows_left }
730
+
731
+ defp render_added_blocks ( state , terminal_width , rows_left ) do
732
+ { content_blocks , { state , rows_left } } =
733
+ state . blocks_to_add
734
+ |> Enum . reverse ( )
735
+ |> Enum . flat_map_reduce ( { state , rows_left } , fn block_id , { state , rows_left } ->
736
+ % {
737
+ rendered_new_state?: true ,
738
+ block_content: block_content ,
739
+ block_height: block_height ,
740
+ new_rows_left: rows_left ,
741
+ content_to_render: content_to_render
742
+ } = get_content ( state , block_id , terminal_width , rows_left )
743
+
744
+ content =
745
+ if content_to_render == [ ] do
746
+ [ ]
747
+ else
748
+ [ content_to_render ]
749
+ end
630
750
631
- { block_content ,
632
- % {
633
- state
634
- | rendered_content_height: Map . put ( state . rendered_content_height , block_id , height ) ,
635
- content: Map . put ( state . content , block_id , block_content )
636
- } }
751
+ { content ,
752
+ { % {
753
+ state
754
+ | rendered_content_height:
755
+ Map . put ( state . rendered_content_height , block_id , % {
756
+ max: block_height ,
757
+ rendered: block_height
758
+ } ) ,
759
+ content: Map . put ( state . content , block_id , block_content )
760
+ } , rows_left } }
637
761
end )
638
762
639
- state = % {
640
- state
641
- | blocks_to_add: [ ] ,
642
- rendered_blocks: state . rendered_blocks ++ state . blocks_to_add
643
- }
644
-
645
- { state , Owl.Data . unlines ( content_blocks ) }
763
+ { state , Owl.Data . unlines ( Enum . reverse ( content_blocks ) ) , rows_left }
646
764
end
647
765
648
766
defp empty_blocks_list? ( state ) do
0 commit comments