The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

SpacingTab.vala in libbirdfont

This file is a part of the Birdfont project.

Contributing

Send patches or pull requests to johan.mattsson.m@gmail.com.
Clone this repository: git clone https://github.com/johanmattssonm/birdfont.git

Revisions

View the latest version of libbirdfont/SpacingTab.vala.
Thread safety in text rendering
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 51 // background 52 cr.save (); 53 cr.set_source_rgba (1, 1, 1, 1); 54 cr.rectangle (0, allocation.height - height, allocation.width, height); 55 cr.fill (); 56 cr.restore (); 57 58 // character bar 59 cr.save (); 60 cr.set_source_rgba (0.5, 0.5, 0.5, 1); 61 cr.set_line_width (0.8); 62 cr.move_to (0, allocation.height - height); 63 cr.line_to (allocation.width, allocation.height - height); 64 cr.stroke (); 65 66 cr.move_to (0, allocation.height - height + character_height); 67 cr.line_to (allocation.width, allocation.height - height + character_height); 68 cr.stroke (); 69 cr.restore (); 70 71 // TODO: add button for processing ligatures 72 row = get_first_row ().process_ligatures (); 73 index = 0; 74 foreach (Glyph? g in row.glyph) { 75 draw_glyph_spacing (allocation, cr, g, index); 76 index++; 77 } 78 } 79 80 void draw_glyph_spacing (WidgetAllocation allocation, Context cr, Glyph? glyph, int index) { 81 Glyph g; 82 double end, middle; 83 double l, r; 84 Text left, right, cap; 85 unichar c; 86 87 this.allocation = allocation; 88 89 // end mark 90 end = (index + 1) * box_size; 91 cr.save (); 92 cr.set_source_rgba (0.5, 0.5, 0.5, 1); 93 cr.set_line_width (2); 94 cr.move_to (end, allocation.height - height); 95 cr.line_to (end, allocation.height); 96 cr.stroke (); 97 cr.restore (); 98 99 // boxes 100 middle = end - box_size / 2.0; 101 cr.save (); 102 cr.set_source_rgba (0.5, 0.5, 0.5, 1); 103 cr.set_line_width (0.8); 104 cr.move_to (middle, allocation.height - height + character_height); 105 cr.line_to (middle, allocation.height); 106 cr.stroke (); 107 cr.restore (); 108 109 if (glyph != null) { 110 g = (!) glyph; 111 112 c = g.get_unichar (); 113 cap = new Text ((!) c.to_string (), 17); 114 cap.set_source_rgba (72 / 255.0, 72 / 255.0, 72 / 255.0, 1); 115 cap.widget_x = middle - cap.get_extent () / 2.0; 116 cap.widget_y = allocation.height - height + character_height - 4 * MainWindow.units; 117 cap.draw_at_baseline (cr, cap.widget_x, cap.widget_y); 118 119 l = g.get_left_side_bearing (); 120 left = new Text (truncate (l, 5), 17); 121 left.set_source_rgba (72 / 255.0, 72 / 255.0, 72 / 255.0, 1); 122 left.widget_x = middle - box_size / 2.0 + (box_size / 2.0 - left.get_extent ()) / 2.0; 123 left.widget_y = allocation.height - 7 * MainWindow.units; 124 left.draw_at_baseline (cr, left.widget_x, left.widget_y); 125 126 r = g.get_right_side_bearing (); 127 right = new Text (truncate (r, 5), 17); 128 right.set_source_rgba (72 / 255.0, 72 / 255.0, 72 / 255.0, 1); 129 right.widget_x = end - (box_size / 2.0 - right.get_extent ()) / 2.0 - right.get_extent (); 130 right.widget_y = allocation.height - 7 * MainWindow.units; 131 right.draw_at_baseline (cr, right.widget_x, right.widget_y); 132 } 133 } 134 135 public string truncate (double f, int digits) { 136 string t = @"$f"; 137 string s = ""; 138 139 int d = digits; 140 int i; 141 unichar c; 142 143 if (t.index_of ("-") != -1) { 144 d++; 145 } 146 147 if (t.index_of (".") != -1) { 148 d++; 149 } 150 151 i = 0; 152 while (t.get_next_char (ref i, out c)) { 153 s = s + (!) c.to_string (); 154 155 if (i >= d) { 156 break; 157 } 158 } 159 160 return s; 161 } 162 163 public override void button_press (uint button, double ex, double ey) { 164 GlyphSequence row; 165 double p; 166 167 if (ey >= allocation.height - height) { 168 // TODO: add button for processing ligatures 169 row = get_first_row ().process_ligatures (); 170 p = 0; 171 foreach (Glyph? g in row.glyph) { 172 if (p < ex < p + box_size / 2.0) { 173 update_lsb(g); 174 } 175 176 if (p + box_size / 2.0 < ex < p + box_size) { 177 update_rsb(g); 178 } 179 180 p += box_size; 181 } 182 } else { 183 base.button_press (button, ex, ey); 184 } 185 } 186 187 public override void button_release (int button, double ex, double ey) { 188 if (button == 3) { 189 return; 190 } 191 192 if (!(ey >= allocation.height - height)) { 193 base.button_release (button, ex, ey); 194 } 195 } 196 197 void update_lsb (Glyph? g) { 198 TextListener listener; 199 string submitted_value = ""; 200 201 if (g == null) { 202 return; 203 } 204 205 text_input_glyph = (!) g; 206 listener = new TextListener (t_("Left"), @"$(text_input_glyph.get_left_side_bearing ())", t_("Set")); 207 208 listener.signal_text_input.connect ((text) => { 209 submitted_value = text; 210 211 if (MenuTab.suppress_event) { 212 return; 213 } 214 215 GlyphCanvas.redraw (); 216 }); 217 218 listener.signal_submit.connect (() => { 219 double v; 220 MainWindow.native_window.hide_text_input (); 221 222 text_input = false; 223 suppress_input = false; 224 225 v = double.parse (submitted_value); 226 text_input_glyph.left_limit -= v - text_input_glyph.get_left_side_bearing (); 227 }); 228 229 suppress_input = true; 230 text_input = true; 231 MainWindow.native_window.set_text_listener (listener); 232 } 233 234 void update_rsb (Glyph? g) { 235 TextListener listener; 236 string submitted_value = ""; 237 238 if (g == null) { 239 return; 240 } 241 242 text_input_glyph = (!) g; 243 listener = new TextListener (t_("Right"), @"$(text_input_glyph.get_right_side_bearing ())", t_("Set")); 244 245 listener.signal_text_input.connect ((text) => { 246 submitted_value = text; 247 248 if (MenuTab.suppress_event) { 249 return; 250 } 251 252 GlyphCanvas.redraw (); 253 }); 254 255 listener.signal_submit.connect (() => { 256 double v; 257 MainWindow.native_window.hide_text_input (); 258 259 text_input = false; 260 suppress_input = false; 261 262 v = double.parse (submitted_value); 263 text_input_glyph.right_limit += v - text_input_glyph.get_right_side_bearing (); 264 }); 265 266 suppress_input = true; 267 text_input = true; 268 MainWindow.native_window.set_text_listener (listener); 269 } 270 } 271 272 } 273