Commit 8655a43
Copy and paste in name and description tab
Changed files (7)
modifiedbirdfont/GtkWindow.valamodifiedlibbirdfont/ClipTool.valamodifiedlibbirdfont/DescriptionDisplay.valamodifiedlibbirdfont/KeyBindings.valamodifiedlibbirdfont/NativeWindow.valamodifiedlibbirdfont/Renderer/TextArea.valamodifiedlibbirdfont/Widget.vala
Diff
@@ -391,6 +391,21 @@ public class GtkWindow : Gtk.Window, NativeWindow { } } + public void set_clipboard_text (string text) {+ clipboard.set_text (text, -1);+ }+ + public string get_clipboard_text () {+ string? t;+ + t = clipboard.wait_for_text ();+ if (t != null) {+ return ((!) t).dup ();+ }+ + return "".dup ();+ }+ public void set_clipboard (string svg) { TargetEntry t = { "image/svg+xml", 0, 0 }; TargetEntry[] targets = { t };@@ -16,11 +16,22 @@ namespace BirdFont { public class ClipTool : Tool { + public static void copy_text (TextArea t) {+ MainWindow.native_window.set_clipboard_text (t.get_selected_text ()); + }++ public static void paste_text (TextArea t) {+ if (t.carret_is_visible) {+ t.insert_text (MainWindow.native_window.get_clipboard_text ());+ }+ }+ public static void copy () { FontDisplay fd = MainWindow.get_current_display (); string svg_data; string bf_data; string data;+ TextArea t; if (fd is Glyph) { svg_data = ExportTool.export_selected_paths_to_svg ();@@ -28,11 +39,9 @@ public class ClipTool : Tool { data = svg_data + bf_data; MainWindow.native_window.set_clipboard (data); MainWindow.native_window.set_inkscape_clipboard (data);- }- - if (fd is OverView) {+ } else if (fd is OverView) { copy_overview_glyphs ();- } + } } /** Copy entire glyph. */@@ -77,6 +86,7 @@ public class ClipTool : Tool { Glyph g = MainWindow.get_current_glyph (); double x, y, w, h; double dx, dy;+ TextArea t; if (fd is Glyph) { paste_paths (false);@@ -89,15 +99,11 @@ public class ClipTool : Tool { foreach (Path path in g.active_paths) { path.move (dx, dy); }- }- - if (fd is KerningDisplay) {+ } else if (fd is KerningDisplay) { paste_letters_to_kerning_tab ();- }- - if (fd is OverView) {+ } else if (fd is OverView) { paste_to_overview ();- } + } } static void paste_paths (bool paste_guide_lines) {@@ -203,29 +203,53 @@ public class DescriptionDisplay : FontDisplay { } focus = (!) keyboard_focus;- if (!KeyBindings.has_alt () && !KeyBindings.has_ctrl ()) {- switch (c) {- case Key.RIGHT:- focus.move_carret_next ();- break;- case Key.LEFT:- focus.move_carret_previous ();- break;- case Key.BACK_SPACE:+ switch (c) {+ case 'a':+ if (KeyBindings.has_ctrl ()) {+ focus.select_all ();+ }+ break;+ case 'c':+ if (KeyBindings.has_ctrl ()) {+ ClipTool.copy_text (focus);+ }+ break;+ case 'v':+ if (KeyBindings.has_ctrl ()) {+ ClipTool.paste_text (focus);+ }+ break;+ case Key.RIGHT:+ focus.move_carret_next ();+ break;+ case Key.LEFT:+ focus.move_carret_previous ();+ break;+ case Key.BACK_SPACE:+ if (focus.has_selection ()) {+ focus.delete_selected_text ();+ } else { focus.remove_last_character ();- break;- case Key.ENTER:- focus.insert_text ("\n");- break; - default:- if (!is_modifier_key (keyval)) {- s = (!) c.to_string ();- if (s.validate () && keyboard_focus != null) {- focus.insert_text (s);- }+ }+ break;+ case Key.ENTER:+ focus.insert_text ("\n");+ break;+ case Key.DEL:+ if (focus.has_selection ()) {+ focus.delete_selected_text ();+ } else {+ focus.remove_next_character ();+ }+ break; + default:+ if (!is_modifier_key (keyval)) {+ s = (!) c.to_string ();+ if (s.validate () && keyboard_focus != null) {+ focus.insert_text (s); }- break;- }+ }+ break; } GlyphCanvas.redraw ();@@ -233,21 +257,20 @@ public class DescriptionDisplay : FontDisplay { public override void button_press (uint button, double x, double y) { TextArea t;+ TextArea old; CheckBox c; - if (keyboard_focus != null) {- t = (!) keyboard_focus;- t.draw_carret = false;- keyboard_focus = null;- }- foreach (Widget w in widgets) {- if (w.widget_x <= x <= w.widget_x + w.get_width ()- && w.widget_y <= y <= w.widget_y + w.get_height ()) {- + if (w.is_over (x, y)) { if (w is TextArea) { t = (TextArea) w;+ if (keyboard_focus != null && (!) keyboard_focus != t) {+ old = (!) keyboard_focus;+ old.draw_carret = false;+ }+ t.draw_carret = true;+ t.button_press (button, x, y); keyboard_focus = t; } else if (w is CheckBox) { c = (CheckBox) w;@@ -260,10 +283,25 @@ public class DescriptionDisplay : FontDisplay { } public override void button_release (int button, double x, double y) {+ TextArea t;+ + if (keyboard_focus != null) {+ t = (!) keyboard_focus;+ t.button_release (button, x, y);+ }+ GlyphCanvas.redraw (); } public override void motion_notify (double x, double y) {+ TextArea t;+ + if (keyboard_focus != null) {+ t = (!) keyboard_focus;+ if (t.motion (x, y)) {+ GlyphCanvas.redraw ();+ }+ } } public override string get_label () {@@ -73,7 +73,8 @@ bool is_modifier_key (uint i) { Key.TAB == i || Key.CAPS_LOCK == i || Key.LOGO_RIGHT == i ||- Key.ENTER == i;+ Key.ENTER == i ||+ Key.DEL == i; } /** Modifier flags */@@ -69,6 +69,13 @@ public interface NativeWindow : GLib.Object { /** Load images in a background thread. */ public abstract void run_background_thread (Task t);+ + /** Copy text to clipboard. */+ public abstract void set_clipboard_text (string text);+ + /** Get text from clipboard. */+ public abstract string get_clipboard_text ();+ } }@@ -25,13 +25,26 @@ public class TextArea : Widget { public double padding = 3.3; public bool single_line = false; - public bool draw_carret = false;+ public bool draw_carret {+ get { return carret_is_visible; }+ set { + carret_is_visible = value; + if (!value) {+ update_selection = false;+ selection_end = carret;+ }+ }+ }+ public bool carret_is_visible = false; public bool draw_border = true; public double width; public double height; int carret = 0;+ int selection_end = -1;+ bool update_selection = false;+ string text = ""; int text_length = 0; @@ -73,6 +86,32 @@ public class TextArea : Widget { text_changed (text); } + public string get_selected_text () {+ int start, stop;+ + if (!has_selection ()) {+ return "".dup ();+ }+ + start = (carret < selection_end) ? carret : selection_end;+ stop = (carret > selection_end) ? carret : selection_end;+ + return text.substring (start, stop - start);+ }+ + public void select_all () {+ carret = 0;+ selection_end = text_length;+ }+ + public void delete_selected_text () {+ int start = (carret < selection_end) ? carret : selection_end;+ int stop = (carret > selection_end) ? carret : selection_end;+ set_text (text.substring (0, start) + text.substring (stop));+ selection_end = -1;+ carret = start;+ }+ public void remove_last_character () { int last_index = 0; int index = 0;@@ -83,15 +122,39 @@ public class TextArea : Widget { } set_text (text.substring (0, last_index) + text.substring (carret));+ selection_end = -1;+ carret = last_index;+ }+ + public void remove_next_character () {+ int index = carret;+ unichar c; + text.get_next_char (ref index, out c);+ set_text (text.substring (0, carret) + text.substring (index)); } public void move_carret_next () { int index = 0;+ int index_space; unichar c; char* s = (char*) text + carret; string n = (string) s;++ if (!has_selection () && KeyBindings.has_shift ()) {+ selection_end = carret;+ } else if (!KeyBindings.has_shift ()) {+ selection_end = -1;+ } n.get_next_char (ref index, out c);+ + if (KeyBindings.has_ctrl ()) {+ index_space = n.index_of (" ", index);+ if (index_space != -1) {+ index = index_space;+ }+ }+ carret += index; } @@ -99,13 +162,31 @@ public class TextArea : Widget { int last_index = 0; int index = 0; unichar c;+ int index_space; while (text.get_next_char (ref index, out c) && index < carret) { last_index = index; } + if (!has_selection () && KeyBindings.has_shift ()) {+ selection_end = carret;+ } else if (!KeyBindings.has_shift ()) {+ selection_end = -1;+ }++ if (KeyBindings.has_ctrl ()) {+ index_space = text.last_index_of (" ", last_index);+ if (index_space != -1) {+ last_index = index_space;+ }+ }+ carret = last_index; }+ + public bool has_selection () {+ return selection_end >= 0 && selection_end != carret;+ } public void insert_text (string t) { string s;@@ -116,6 +197,10 @@ public class TextArea : Widget { s = t; } + if (has_selection ()) {+ delete_selected_text ();+ }+ string nt = text.substring (0, carret); int tl = s.length; nt += s;@@ -127,7 +212,8 @@ public class TextArea : Widget { text_changed (text); } - public void layout () {+ /** @return offset to click in text. */+ public int layout (double click_x = -1, double click_y = -1) { Text word; double p; double tx, ty;@@ -136,15 +222,16 @@ public class TextArea : Widget { double width = this.width - 2 * padding; double height = this.height - 2 * padding; bool carret_visibility;- - iter_pos = 0;+ int carret_position = text_length;+ word = new Text (); word.set_font_size (font_size); tx = 0; ty = font_size;+ iter_pos = 0; while (iter_pos < text_length) {- w = get_next_word (out carret_visibility);+ w = get_next_word (out carret_visibility, ref iter_pos); if (w == "") { break;@@ -161,6 +248,11 @@ public class TextArea : Widget { } } + if (widget_y + ty - font_size <= click_y <= widget_y + ty + padding+ && widget_x + tx <= click_x) {+ carret_position = find_carret_pos_in_word (word, iter_pos, tx, click_x);+ }+ if (w != "\n") { tx += p; }@@ -177,6 +269,63 @@ public class TextArea : Widget { } this.height = fmax (min_height, ty + 2 * padding);++ if (click_x > 0 && click_x < widget_x + padding) {+ carret_position = 0;+ }+ + return carret_position;+ }+ + private int find_carret_pos_in_word (Text word, int iter_pos, double tx, double click_x) {+ double wx = widget_x + tx + padding;+ double ratio = word.get_scale ();+ int i = 0;+ double d = 0;+ double min_d = Math.fabs (click_x - wx);+ int carret_position;+ string w = word.text;+ + carret_position = iter_pos - w.length;+ word.iterate ((glyph, kerning, last) => {+ glyph.add_help_lines ();+ + i += ((!) glyph.get_unichar ().to_string ()).length;+ wx += (glyph.get_width () + kerning) * ratio;+ d = Math.fabs (wx - click_x);+ if (d < min_d) {+ min_d = d;+ carret_position = iter_pos - w.length + i;+ }+ });+ + d = Math.fabs (click_x - wx);+ if (d < min_d) {+ min_d = d;+ carret_position = iter_pos + i;+ }+ + return carret_position;+ }+ + public void button_press (uint button, double x, double y) {+ if (is_over (x, y)) {+ carret = layout (x, y);+ selection_end = carret;+ update_selection = true;+ }+ }++ public void button_release (uint button, double x, double y) {+ update_selection = false;+ }+ + public bool motion (double x, double y) {+ if (update_selection) {+ selection_end = layout (x, y);+ }+ + return update_selection; } public override void draw (Context cr) {@@ -189,6 +338,8 @@ public class TextArea : Widget { double width; double x = widget_x; double y = widget_y;+ int selection_start, selection_stop;+ unichar c; layout (); @@ -210,8 +361,7 @@ public class TextArea : Widget { } cr.save ();- - iter_pos = 0;+ word = new Text (); width = this.width - padding;@@ -220,14 +370,71 @@ public class TextArea : Widget { scale = word.get_scale (); y += font_size; - if (draw_carret && iter_pos == 0 && text_length == 0) {- draw_carret_at (cr, x + padding, y + padding);+ if (draw_carret && carret == 0) {+ draw_carret_at (cr, x + padding, y + padding - 1);+ }+ + // draw selection background+ if (selection_end >= 0 && selection_end != carret) {+ tx = 0;+ ty = 0;++ selection_start = carret < selection_end ? carret : selection_end;+ selection_stop = carret > selection_end ? carret : selection_end;+ + cr.save ();+ cr.set_source_rgba (234 / 255.0, 77 / 255.0, 26 / 255.0, 1);+ iter_pos = 0;+ + tx = 0;+ ty = 0;+ iter_pos = 0;+ while (iter_pos < text_length) {+ w = get_next_word (out carret_at_end_of_word, ref iter_pos);+ + if (w == "") {+ break;+ }+ + word.set_text (w);+ p = word.get_sidebearing_extent ();+ + if (tx + p > width || w == "\n") {+ tx = 0;+ ty += font_size;+ }+ + if (w != "\n") {+ iter_pos -= w.length;+ word.iterate ((glyph, kerning, last) => {+ double selection_y;+ double cw;+ + glyph.add_help_lines ();+ + iter_pos += ((!) glyph.get_unichar ().to_string ()).length;+ cw = (glyph.get_width () + kerning) * word.get_scale ();+ tx += cw;+ + if (selection_start < iter_pos <= selection_stop) { + selection_y = y + ty + scale * - word.font.bottom_limit - font_size;+ cr.rectangle (x + tx - 1 - cw, selection_y, cw + 1, font_size);+ cr.fill ();+ }+ });+ }+ }+ + cr.restore (); } ++ // draw characters tx = 0; ty = 0;+ iter_pos = 0; while (iter_pos < text_length) {- w = get_next_word (out carret_at_end_of_word);+ w = get_next_word (out carret_at_end_of_word, ref iter_pos); if (w == "") { break;@@ -263,7 +470,20 @@ public class TextArea : Widget { cr.restore (); } - string get_next_word (out bool carret_at_end_of_word) {+ unichar get_next_char (ref int iter_pos) {+ unichar n;++ if (iter_pos >= text_length) {+ return '\0';+ }+ + n = text.get_char (iter_pos);+ iter_pos += ((!) n.to_string ()).length; + + return n;+ }+ + string get_next_word (out bool carret_at_end_of_word, ref int iter_pos) { int i; int ni; int pi;@@ -42,6 +42,11 @@ public abstract class Widget : GLib.Object { cr.line_to (x, y + radius); cr.close_path (); }++ public bool is_over (double x, double y) {+ return widget_x <= x <= widget_x + get_width () + && widget_y <= y <= widget_y + get_height ();+ } } }