aboutsummaryrefslogtreecommitdiff
path: root/src/tui/layout.hh
blob: 5feb0cd45e5152203c43fcf7f8c27b8595991601 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/*
 * Ricochet - A minimalist CLI web browser for fast, distraction-free navigation.
 * Copyright (C) 2026  Richard Qin
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#pragma once

#include "paint.hh"
#include "ricochet/tui/view.hh"
#include "text.hh"

#include <cstddef>
#include <cstdint>
#include <optional>
#include <span>
#include <variant>
#include <vector>

namespace ricochet::tui::detail {

using HitTarget = std::variant<LinkId, ControlId>;

struct LayoutPoint
{
  std::size_t row { 0 };
  std::int64_t column { 0 };
};

struct LayoutHit
{
  DisplayPosition position {};
  DocumentRange unit {};
  std::optional<HitTarget> target {};
};

struct LayoutGlyph
{
  LayoutPoint position {};
  int columns { 0 };
  std::wstring text {};
  TextStyle style {};
  DocumentRange source {};
  std::optional<HitTarget> target {};
  // Clip bounds are in document-layout columns and also constrain hit testing.
  std::int64_t clip_left { 0 };
  std::int64_t clip_right { 0 };
  // Structural boundaries locate anchors but do not displace nearby text hits.
  bool boundary_only { false };
  std::int64_t line_start { 0 };
  bool decoration { false };
  bool line_break { false };
  bool fill { false };
};

struct LayoutData
{
  // Reading order, including table cells in row/cell order; not screen order.
  std::vector<LayoutGlyph> glyphs {};
  std::size_t rows { 1 };
  std::int64_t extent { 0 };
};

// A derived snapshot for one Document + initial control values + width/scrolls.
// Editing will supply a separate draft snapshot; this stage uses revision zero.
// No ncurses headers, terminal objects or business-module dependencies.
class DocumentLayout
{
public:
  DocumentLayout( const Document& document,
                  const TextService& text,
                  int columns,
                  std::span<const HorizontalScroll> scrolls );

  int columns() const;
  std::size_t rows() const;
  std::optional<LayoutPoint> locate( const DisplayPosition& position ) const;
  std::optional<LayoutHit> hit( LayoutPoint point ) const;
  void append_frame( Frame& frame, std::size_t first_row, CellPoint origin, ScreenSize viewport ) const;

private:
  int columns_;
  LayoutData data_;
};

} // namespace ricochet::tui::detail