.
1 /*
2 Copyright (C) 2015 Johan Mattsson
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 3 of the
7 License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13 */
14
15 using Cairo;
16
17 namespace BirdFont {
18
19 /** Kerning context. */
20 public class SpacingTab : KerningDisplay {
21
22 double box_size = 122 * MainWindow.units;
23 double height = 44 * MainWindow.units;
24 double character_height = 20 * MainWindow.units;
25
26 WidgetAllocation allocation;
27
28 Glyph text_input_glyph;
29
30 public SpacingTab () {
31 adjust_side_bearings = true;
32 }
33
34 public override string get_label () {
35 return t_("Spacing");
36 }
37
38 public override string get_name () {
39 return "Spacing";
40 }
41
42 public override void draw (WidgetAllocation allocation, Context cr) {
43 base.draw (allocation, cr);
44 draw_spacing_metrix (allocation, cr);
45 }
46
47 void draw_spacing_metrix (WidgetAllocation allocation, Context cr) {
48 GlyphSequence row;
49 int index;
50 Font font = BirdFont.get_current_font ();
51
52 // background
53 cr.save ();
54 cr.set_source_rgba (1, 1, 1, 1);
55 cr.rectangle (0, allocation.height - height, allocation.width, height);
56 cr.fill ();
57 cr.restore ();
58
59 // character bar
60 cr.save ();
61 cr.set_source_rgba (0.5, 0.5, 0.5, 1);
62 cr.set_line_width (0.8);
63 cr.move_to (0, allocation.height - height);
64 cr.line_to (allocation.width, allocation.height - height);
65 cr.stroke ();
66
67 cr.move_to (0, allocation.height - height + character_height);
68 cr.line_to (allocation.width, allocation.height - height + character_height);
69 cr.stroke ();
70 cr.restore ();
71
72 // TODO: add button for processing ligatures
73 row = get_first_row ().process_ligatures (font);
74 index = 0;
75 foreach (Glyph? g in row.glyph) {
76 draw_glyph_spacing (allocation, cr, g, index);
77 index++;
78 }
79 }
80
81 void draw_glyph_spacing (WidgetAllocation allocation, Context cr, Glyph? glyph, int index) {
82 Glyph g;
83 double end, middle;
84 double l, r;
85 Text left, right, cap;
86 unichar c;
87
88 this.allocation = allocation;
89
90 // end mark
91 end = (index + 1) * box_size;
92 cr.save ();
93 cr.set_source_rgba (0.5, 0.5, 0.5, 1);
94 cr.set_line_width (2);
95 cr.move_to (end, allocation.height - height);
96 cr.line_to (end, allocation.height);
97 cr.stroke ();
98 cr.restore ();
99
100 // boxes
101 middle = end - box_size / 2.0;
102 cr.save ();
103 cr.set_source_rgba (0.5, 0.5, 0.5, 1);
104 cr.set_line_width (0.8);
105 cr.move_to (middle, allocation.height - height + character_height);
106 cr.line_to (middle, allocation.height);
107 cr.stroke ();
108 cr.restore ();
109
110 if (glyph != null) {
111 g = (!) glyph;
112
113 c = g.get_unichar ();
114 cap = new Text ((!) c.to_string (), 17);
115 cap.set_source_rgba (72 / 255.0, 72 / 255.0, 72 / 255.0, 1);
116 cap.widget_x = middle - cap.get_extent () / 2.0;
117 cap.widget_y = allocation.height - height + character_height - 4 * MainWindow.units;
118 cap.draw_at_baseline (cr, cap.widget_x, cap.widget_y);
119
120 l = g.get_left_side_bearing ();
121 left = new Text (truncate (l, 5), 17);
122 left.set_source_rgba (72 / 255.0, 72 / 255.0, 72 / 255.0, 1);
123 left.widget_x = middle - box_size / 2.0 + (box_size / 2.0 - left.get_extent ()) / 2.0;
124 left.widget_y = allocation.height - 7 * MainWindow.units;
125 left.draw_at_baseline (cr, left.widget_x, left.widget_y);
126
127 r = g.get_right_side_bearing ();
128 right = new Text (truncate (r, 5), 17);
129 right.set_source_rgba (72 / 255.0, 72 / 255.0, 72 / 255.0, 1);
130 right.widget_x = end - (box_size / 2.0 - right.get_extent ()) / 2.0 - right.get_extent ();
131 right.widget_y = allocation.height - 7 * MainWindow.units;
132 right.draw_at_baseline (cr, right.widget_x, right.widget_y);
133 }
134 }
135
136 public string truncate (double f, int digits) {
137 string t = @"$f";
138 string s = "";
139
140 int d = digits;
141 int i;
142 unichar c;
143
144 if (t.index_of ("-") != -1) {
145 d++;
146 }
147
148 if (t.index_of (".") != -1) {
149 d++;
150 }
151
152 i = 0;
153 while (t.get_next_char (ref i, out c)) {
154 s = s + (!) c.to_string ();
155
156 if (i >= d) {
157 break;
158 }
159 }
160
161 return s;
162 }
163
164 public override void button_press (uint button, double ex, double ey) {
165 GlyphSequence row;
166 double p;
167 Font font = BirdFont.get_current_font ();
168
169 if (ey >= allocation.height - height) {
170 // TODO: add button for processing ligatures
171 row = get_first_row ().process_ligatures (font);
172 p = 0;
173 foreach (Glyph? g in row.glyph) {
174 if (p < ex < p + box_size / 2.0) {
175 update_lsb(g);
176 }
177
178 if (p + box_size / 2.0 < ex < p + box_size) {
179 update_rsb(g);
180 }
181
182 p += box_size;
183 }
184 } else {
185 base.button_press (button, ex, ey);
186 }
187 }
188
189 public override void button_release (int button, double ex, double ey) {
190 if (button == 3) {
191 return;
192 }
193
194 if (!(ey >= allocation.height - height)) {
195 base.button_release (button, ex, ey);
196 }
197 }
198
199 void update_lsb (Glyph? g) {
200 TextListener listener;
201 string submitted_value = "";
202
203 if (g == null) {
204 return;
205 }
206
207 text_input_glyph = (!) g;
208 listener = new TextListener (t_("Left"), @"$(text_input_glyph.get_left_side_bearing ())", t_("Set"));
209
210 listener.signal_text_input.connect ((text) => {
211 submitted_value = text;
212
213 if (MenuTab.suppress_event) {
214 return;
215 }
216
217 GlyphCanvas.redraw ();
218 });
219
220 listener.signal_submit.connect (() => {
221 double v;
222 MainWindow.native_window.hide_text_input ();
223
224 text_input = false;
225 suppress_input = false;
226
227 v = double.parse (submitted_value);
228 text_input_glyph.left_limit -= v - text_input_glyph.get_left_side_bearing ();
229 });
230
231 suppress_input = true;
232 text_input = true;
233 MainWindow.native_window.set_text_listener (listener);
234 }
235
236 void update_rsb (Glyph? g) {
237 TextListener listener;
238 string submitted_value = "";
239
240 if (g == null) {
241 return;
242 }
243
244 text_input_glyph = (!) g;
245 listener = new TextListener (t_("Right"), @"$(text_input_glyph.get_right_side_bearing ())", t_("Set"));
246
247 listener.signal_text_input.connect ((text) => {
248 submitted_value = text;
249
250 if (MenuTab.suppress_event) {
251 return;
252 }
253
254 GlyphCanvas.redraw ();
255 });
256
257 listener.signal_submit.connect (() => {
258 double v;
259 MainWindow.native_window.hide_text_input ();
260
261 text_input = false;
262 suppress_input = false;
263
264 v = double.parse (submitted_value);
265 text_input_glyph.right_limit += v - text_input_glyph.get_right_side_bearing ();
266 });
267
268 suppress_input = true;
269 text_input = true;
270 MainWindow.native_window.set_text_listener (listener);
271 }
272 }
273
274 }
275