nvim_gtk/render/
context.rs1use std::collections::HashSet;
2
3use pango;
4
5use crate::sys::pango as sys_pango;
6
7use super::itemize::ItemizeIterator;
8use crate::ui_model::StyledLine;
9
10pub struct Context {
11 font_metrics: FontMetrix,
12 font_features: FontFeatures,
13 line_space: i32,
14}
15
16impl Context {
17 pub fn new(pango_context: pango::Context) -> Self {
18 Context {
19 line_space: 0,
20 font_metrics: FontMetrix::new(pango_context, 0),
21 font_features: FontFeatures::new(),
22 }
23 }
24
25 pub fn update(&mut self, pango_context: pango::Context) {
26 self.font_metrics = FontMetrix::new(pango_context, self.line_space);
27 }
28
29 pub fn update_font_features(&mut self, font_features: FontFeatures) {
30 self.font_features = font_features;
31 }
32
33 pub fn update_line_space(&mut self, line_space: i32) {
34 self.line_space = line_space;
35 let pango_context = self.font_metrics.pango_context.clone();
36 self.font_metrics = FontMetrix::new(pango_context, self.line_space);
37 }
38
39 pub fn itemize(&self, line: &StyledLine) -> Vec<pango::Item> {
40 let attr_iter = line.attr_list.get_iterator();
41
42 ItemizeIterator::new(&line.line_str)
43 .flat_map(|(offset, len)| {
44 pango::itemize(
45 &self.font_metrics.pango_context,
46 &line.line_str,
47 offset as i32,
48 len as i32,
49 &line.attr_list,
50 attr_iter.as_ref(),
51 )
52 })
53 .collect()
54 }
55
56 pub fn create_layout(&self) -> pango::Layout {
57 pango::Layout::new(&self.font_metrics.pango_context)
58 }
59
60 pub fn font_description(&self) -> &pango::FontDescription {
61 &self.font_metrics.font_desc
62 }
63
64 pub fn cell_metrics(&self) -> &CellMetrics {
65 &self.font_metrics.cell_metrics
66 }
67
68 pub fn font_features(&self) -> &FontFeatures {
69 &self.font_features
70 }
71
72 pub fn font_families(&self) -> HashSet<glib::GString> {
73 self.font_metrics
74 .pango_context
75 .list_families()
76 .iter()
77 .filter_map(pango::FontFamilyExt::get_name)
78 .collect()
79 }
80}
81
82struct FontMetrix {
83 pango_context: pango::Context,
84 cell_metrics: CellMetrics,
85 font_desc: pango::FontDescription,
86}
87
88impl FontMetrix {
89 pub fn new(pango_context: pango::Context, line_space: i32) -> Self {
90 let font_metrics = pango_context.get_metrics(None, None).unwrap();
91 let font_desc = pango_context.get_font_description().unwrap();
92
93 FontMetrix {
94 pango_context,
95 cell_metrics: CellMetrics::new(&font_metrics, line_space),
96 font_desc,
97 }
98 }
99}
100
101pub struct CellMetrics {
102 pub line_height: f64,
103 pub char_width: f64,
104 pub ascent: f64,
105 pub underline_position: f64,
106 pub underline_thickness: f64,
107 pub strikethrough_position: f64,
108 pub strikethrough_thickness: f64,
109 pub pango_ascent: i32,
110 pub pango_descent: i32,
111 pub pango_char_width: i32,
112}
113
114impl CellMetrics {
115 fn new(font_metrics: &pango::FontMetrics, line_space: i32) -> Self {
116 let ascent = (f64::from(font_metrics.get_ascent()) / f64::from(pango::SCALE)).ceil();
117 let descent = (f64::from(font_metrics.get_descent()) / f64::from(pango::SCALE)).ceil();
118
119 let pango_underline_position = f64::from(font_metrics.get_underline_position());
121 let underline_position = (pango_underline_position / f64::from(pango::SCALE))
122 .abs()
123 .ceil()
124 .copysign(pango_underline_position);
125
126 let underline_thickness =
127 (f64::from(font_metrics.get_underline_thickness()) / f64::from(pango::SCALE)).ceil();
128
129 let strikethrough_position =
130 (f64::from(font_metrics.get_strikethrough_position()) / f64::from(pango::SCALE)).ceil();
131 let strikethrough_thickness = (f64::from(font_metrics.get_strikethrough_thickness())
132 / f64::from(pango::SCALE))
133 .ceil();
134
135 CellMetrics {
136 pango_ascent: font_metrics.get_ascent(),
137 pango_descent: font_metrics.get_descent(),
138 pango_char_width: font_metrics.get_approximate_char_width(),
139 ascent,
140 line_height: ascent + descent + f64::from(line_space),
141 char_width: f64::from(font_metrics.get_approximate_char_width())
142 / f64::from(pango::SCALE),
143 underline_position: ascent - underline_position + underline_thickness / 2.0,
144 underline_thickness,
145 strikethrough_position: ascent - strikethrough_position + strikethrough_thickness / 2.0,
146 strikethrough_thickness,
147 }
148 }
149
150 #[cfg(test)]
151 pub fn new_hw(line_height: f64, char_width: f64) -> Self {
152 CellMetrics {
153 pango_ascent: 0,
154 pango_descent: 0,
155 pango_char_width: 0,
156 ascent: 0.0,
157 line_height,
158 char_width,
159 underline_position: 0.0,
160 underline_thickness: 0.0,
161 strikethrough_position: 0.0,
162 strikethrough_thickness: 0.0,
163 }
164 }
165}
166
167pub struct FontFeatures {
168 attr: Option<pango::Attribute>,
169}
170
171impl FontFeatures {
172 pub fn new() -> Self {
173 FontFeatures { attr: None }
174 }
175
176 pub fn from(font_features: String) -> Self {
177 if font_features.trim().is_empty() {
178 return Self::new();
179 }
180
181 FontFeatures {
182 attr: sys_pango::attribute::new_features(&font_features),
183 }
184 }
185
186 pub fn insert_into(&self, attr_list: &pango::AttrList) {
187 if let Some(ref attr) = self.attr {
188 attr_list.insert(attr.clone());
189 }
190 }
191}