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 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