The Birdfont Source Code


All Repositories / birdfont.git / blob – RSS feed

SpinButton.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
Circle boundaries heads/master
1 /* 2 Copyright (C) 2012, 2014 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 public class SpinButton : Tool { 20 21 public signal void new_value_action (SpinButton selected); 22 23 bool negative = false; 24 25 public int8 n0 = 2; 26 public int8 n1 = 0; 27 public int8 n2 = 0; 28 public int8 n3 = 0; 29 public int8 n4 = 0; 30 31 bool value_from_motion = false; 32 double begin_y = 0; 33 int begin_value = 0; 34 35 int max = 99999; 36 int min = 0; 37 int step = 1; 38 39 bool big_number = false; 40 41 double last_active_time = 0; 42 bool waiting_for_icon_switch = false; 43 bool show_icon_tool_icon = false; 44 45 /** Lock the button to a fixed value. */ 46 public bool locked = false; 47 48 static Gee.ArrayList<Text> digits; 49 static double text_height = 14; 50 static Text period; 51 static Text comma; 52 static Text minus; 53 54 public SpinButton (string? name = null, string tip = "") { 55 base (null , tip); 56 57 if (name != null) { 58 base.name = (!) name; 59 } 60 61 set_icon ("spin_button"); 62 63 panel_press_action.connect ((selected, button, tx, ty) => { 64 double py = Math.fabs (y - ty); 65 int n = 0; 66 67 if (button == 3 || KeyBindings.modifier != NONE) { 68 set_from_text (); 69 n = 0; 70 set_selected (false); 71 return; 72 } 73 74 if (is_selected ()) { 75 if (button == 1) { 76 n = 1; 77 } else if (button == 2) { 78 n = 10; 79 } 80 81 for (int i = 0; i < n; i++) { 82 if (py < 9 && !locked) { 83 increase (); 84 } 85 86 if (py > 25 && !locked) { 87 decrease (); 88 } 89 } 90 } 91 92 value_from_motion = !locked; 93 94 begin_y = ty; 95 96 begin_value = get_int_value (); 97 98 if (button == 1) { 99 set_selected (true); 100 } 101 102 redraw (); 103 }); 104 105 panel_move_action.connect ((selected, button, tx, ty) => { 106 double d; 107 int new_value; 108 109 if (is_active ()) { 110 show_adjustmet_icon (); 111 } 112 113 if (value_from_motion && is_selected ()) { 114 d = (begin_y - ty) / 200; 115 d = (d < 0) ? -Math.pow (d, 2) : Math.pow (d, 2); 116 d *= 1000; 117 118 new_value = (int)(begin_value + d); 119 120 if (new_value < min) { 121 set_int_value (@"$min"); 122 } else if (new_value > max) { 123 set_int_value (@"$max"); 124 } else { 125 set_int_value (@"$new_value"); 126 } 127 128 redraw (); 129 } 130 131 return value_from_motion; 132 }); 133 134 panel_release_action.connect ((selected, button, tx, ty) => { 135 value_from_motion = false; 136 137 if (button == 1) { 138 set_selected (false); 139 } 140 141 redraw (); 142 }); 143 144 scroll_wheel_up_action.connect ((selected) => { 145 increase (); 146 return true; 147 }); 148 149 scroll_wheel_down_action.connect ((selected) => { 150 decrease (); 151 return true; 152 }); 153 154 if (is_null (digits)) { 155 add_digits (); 156 } 157 } 158 159 void add_digits () { 160 digits = new Gee.ArrayList<Text> (); 161 162 for (int i = 0; i < 10; i++) { 163 Text digit = new Text (@"$i", text_height); 164 digits.add (digit); 165 } 166 167 period = new Text (".", text_height); 168 comma = new Text (",", text_height); 169 minus = new Text ("-", text_height); 170 } 171 172 public void show_icon (bool i) { 173 show_icon_tool_icon = i; 174 175 if (!show_icon_tool_icon) { 176 set_icon ("spin_button"); 177 } else { 178 set_icon ((!) base.name); 179 } 180 } 181 182 public void hide_value () { 183 set_icon (base.name); 184 waiting_for_icon_switch = false; 185 redraw (); 186 } 187 188 void show_adjustmet_icon () { 189 TimeoutSource timer; 190 191 set_icon ("spin_button"); 192 redraw (); 193 194 last_active_time = GLib.get_real_time (); 195 196 if (show_icon_tool_icon && !waiting_for_icon_switch) { 197 waiting_for_icon_switch = true; 198 199 timer = new TimeoutSource (100); 200 timer.set_callback (() => { 201 if (GLib.get_real_time () - last_active_time > 4000000) { 202 set_icon (base.name); 203 redraw (); 204 waiting_for_icon_switch = false; 205 } 206 207 return waiting_for_icon_switch; 208 }); 209 210 timer.attach (null); 211 } 212 } 213 214 public void set_big_number (bool b) { 215 big_number = b; 216 } 217 218 public static string convert_to_string (double val) { 219 SpinButton sb = new SpinButton (); 220 sb.set_value_round (val); 221 return sb.get_display_value (); 222 } 223 224 public static double convert_to_double (string val) { 225 SpinButton sb = new SpinButton (); 226 sb.set_int_value (val); 227 return sb.get_value (); 228 } 229 230 public void set_from_text () { 231 TextListener listener = new TextListener (t_("Set"), get_display_value (), t_("Close")); 232 233 listener.signal_text_input.connect ((text) => { 234 set_value (text); 235 redraw (); 236 }); 237 238 listener.signal_submit.connect (() => { 239 TabContent.hide_text_input (); 240 redraw (); 241 }); 242 243 TabContent.show_text_input (listener); 244 } 245 246 public void set_max (double max) { 247 if (big_number) { 248 max /= 100; 249 } 250 this.max = (int) Math.rint (max * 10000); 251 } 252 253 public void set_min (double min) { 254 if (big_number) { 255 min /= 100; 256 } 257 this.min = (int) Math.rint (min * 10000); 258 } 259 260 public void set_int_step (double step) { 261 if (big_number) { 262 step /= 100; 263 } 264 this.step = (int) Math.rint (step * 10000); 265 } 266 267 public void increase () { 268 int v; 269 270 v = get_int_value (); 271 v += step; 272 273 if (v > max) { 274 set_int_value (@"$max"); 275 } else { 276 set_int_value (@"$v"); 277 } 278 279 new_value_action (this); 280 redraw (); 281 } 282 283 public void decrease () { 284 int v; 285 286 v = get_int_value (); 287 v -= step; 288 289 if (v <= min) { 290 set_int_value (@"$min"); 291 } else { 292 set_int_value (@"$v"); 293 } 294 295 new_value_action (this); 296 redraw (); 297 } 298 299 public void set_int_value (string new_value) { 300 string v = new_value; 301 302 negative = v.has_prefix ("-"); 303 if (negative) { 304 v = v.replace ("-", ""); 305 } 306 307 while (!(v.char_count () >= 5)) { 308 v = "0" + v; 309 } 310 311 n0 = parse (v.substring (v.index_of_nth_char (0), 1)); 312 n1 = parse (v.substring (v.index_of_nth_char (1), 1)); 313 n2 = parse (v.substring (v.index_of_nth_char (2), 1)); 314 n3 = parse (v.substring (v.index_of_nth_char (3), 1)); 315 n4 = parse (v.substring (v.index_of_nth_char (4), 1)); 316 317 show_adjustmet_icon (); 318 new_value_action (this); 319 } 320 321 int8 parse (string s) { 322 int v = int.parse (s); 323 if (v < 0) { 324 warning ("Failed to parse integer."); 325 return 0; 326 } 327 return (int8) v; 328 } 329 330 public void set_value (string new_value, bool check_boundaries = true, bool emit_signal = true) { 331 string v = new_value.replace (",", "."); 332 int fv; 333 string separator = ""; 334 335 negative = v.has_prefix ("-"); 336 if (negative) { 337 v = v.replace ("-", ""); 338 } 339 340 if (big_number) { 341 if (v == "" || v == "0") { 342 v = "0.0000"; 343 } 344 345 while (v.has_prefix ("0") && !v.has_prefix ("0.")) { 346 v = v.substring (v.index_of_nth_char (1)); 347 } 348 349 fv = int.parse (v); 350 fv = (fv < 0) ? -fv : fv; 351 352 if (fv < 10) { 353 v = @"00$v"; 354 } else if (fv < 100) { 355 v = @"0$v"; 356 } 357 358 v = @"$v"; 359 } 360 361 while (v.char_count () < 6) { 362 if (v.index_of (".") == -1) { 363 v += "."; 364 } else { 365 v += "0"; 366 } 367 } 368 369 if (!big_number) { 370 n0 = (int8) int.parse (v.substring (v.index_of_nth_char (0), 1)); 371 separator = v.substring (v.index_of_nth_char (1), 1); 372 n1 = (int8) int.parse (v.substring (v.index_of_nth_char (2), 1)); 373 n2 = (int8) int.parse (v.substring (v.index_of_nth_char (3), 1)); 374 n3 = (int8) int.parse (v.substring (v.index_of_nth_char (4), 1)); 375 n4 = (int8) int.parse (v.substring (v.index_of_nth_char (5), 1)); 376 } else { 377 n0 = (int8) int.parse (v.substring (v.index_of_nth_char (0), 1)); 378 n1 = (int8) int.parse (v.substring (v.index_of_nth_char (1), 1)); 379 n2 = (int8) int.parse (v.substring (v.index_of_nth_char (2), 1)); 380 separator = v.substring (v.index_of_nth_char (3), 1); 381 n3 = (int8) int.parse (v.substring (v.index_of_nth_char (4), 1)); 382 n4 = (int8) int.parse (v.substring (v.index_of_nth_char (5), 1)); 383 } 384 385 if (separator != ".") { 386 warning (@"Expecting \".\" $new_value -> ($(v))"); 387 } 388 389 if (check_boundaries && get_int_value () > max) { 390 warning (@"Out of bounds ($new_value > $max)."); 391 set_value_round (max, false); 392 } 393 394 if (check_boundaries && get_int_value () < min) { 395 warning (@"Out of bounds ($new_value < $min)."); 396 set_value_round (min, false); 397 } 398 399 if (emit_signal) { 400 new_value_action (this); 401 } 402 403 show_adjustmet_icon (); 404 } 405 406 public void set_value_round (double v, bool check_boundaries = true, bool emit_signal = true) { 407 if (v == -0) { 408 v = 0; 409 } 410 411 set_value (@"$v".replace (",", "."), check_boundaries, emit_signal); 412 } 413 414 public double get_value () { 415 double r; 416 417 if (!big_number) { 418 r = n0 + (n1 / 10.0) + (n2 / 100.0) + (n3 / 1000.0) + (n4 / 1000.0); 419 } else { 420 r = (n0 * 100) + (n1 * 10) + n2 + (n3 / 10.0) + (n4 / 100.0); 421 } 422 423 return (negative) ? -r : r; 424 } 425 426 private int get_int_value () { 427 int r = n0 * 10000 + n1 * 1000 + n2 * 100 + n3 * 10 + n4; 428 return (negative) ? -r : r; 429 } 430 431 public string get_short_display_value () { 432 if (!big_number) { 433 return @"$n0.$n1$n2$n3"; 434 } 435 436 if (negative) { 437 if (n0 == 0 && n1 == 0) { 438 return @"-$n2.$n3$n4"; 439 } 440 441 if (n0 == 0) { 442 return @"-$n1$n2.$n3"; 443 } 444 445 return @"-$n0$n1$n2"; 446 } 447 448 if (n0 == 0 && n1 == 0) { 449 return @"$n2.$n3$n4"; 450 } 451 452 if (n0 == 0) { 453 return @"$n1$n2.$n3$n4"; 454 } 455 456 return @"$n0$n1$n2.$n3"; 457 } 458 459 public string get_display_value () { 460 string v; 461 462 if (!big_number) { 463 return @"$n0.$n1$n2$n3$n4"; 464 } 465 466 v = (negative) ? "-" : ""; 467 468 if (n0 == 0 && n1 == 0) { 469 v = @"$v$n2.$n3$n4"; 470 } else if (n0 == 0) { 471 v = @"$v$n1$n2.$n3$n4"; 472 } else { 473 v = @"$v$n0$n1$n2.$n3$n4"; 474 } 475 476 return v; 477 } 478 479 Text get_glyph (unichar character) { 480 Text text; 481 482 if ('0' <= character <= '9') { 483 int digit_index = int.parse ((!) character.to_string ()); 484 text = digits.get (digit_index); 485 } else if (character == '.') { 486 text = period; 487 } else if (character == ',') { 488 text = comma; 489 } else if (character == '-') { 490 text = minus; 491 } else { 492 text = new Text ((!) character.to_string (), text_height); 493 } 494 495 return text; 496 } 497 498 public override void draw_tool (Context cr, double px, double py) { 499 double x = x - px; 500 double y = y - py; 501 string display_value = get_short_display_value (); 502 503 if (!show_icon_tool_icon || waiting_for_icon_switch) { 504 if (is_selected ()) { 505 base.icon_color = "Active Spin Button"; 506 } else { 507 base.icon_color = "Spin Button"; 508 } 509 } else { 510 if (is_selected ()) { 511 base.icon_color = "Selected Tool Foreground"; 512 } else { 513 base.icon_color = "Tool Foreground"; 514 } 515 } 516 517 base.draw_tool (cr, px, py); 518 519 if (!show_icon_tool_icon || waiting_for_icon_switch) { 520 unichar digit; 521 int index; 522 Text text; 523 double extent = 0; 524 double decender = 0; 525 double carret = 0; 526 double total_extent = 0; 527 double x_offset; 528 529 index = 0; 530 while (display_value.get_next_char (ref index, out digit)) { 531 text = get_glyph (digit); 532 total_extent += text.get_sidebearing_extent (); 533 } 534 535 x_offset = (w - total_extent) / 2 + 1; 536 537 index = 0; 538 while (display_value.get_next_char (ref index, out digit)) { 539 text = get_glyph (digit); 540 extent = text.get_sidebearing_extent (); 541 542 if (decender < text.get_decender ()) { 543 decender = text.get_decender (); 544 } 545 546 if (is_selected ()) { 547 Theme.text_color (text, "Selected Tool Foreground"); 548 } else { 549 Theme.text_color (text, "Tool Foreground"); 550 } 551 552 double text_x = x + carret + x_offset;; 553 double text_y = y + (h - text_height) / 2; 554 555 text.widget_x = text_x; 556 text.widget_y = text_y + decender; 557 text.draw (cr); 558 559 carret += extent; 560 561 } 562 } 563 } 564 } 565 566 } 567