Clone in a terminal:
Commit b5325fb
Johan Mattsson - 2014-11-21T12:58:18+00:00
b5325fb815f5138d81a4d2dbc3cc77f5f7a2f440johan.mattsson.m@gmail.com

Add carret to text area

Changed files (3)

Diff

diff --git a/libbirdfont/DescriptionDisplay.vala b/libbirdfont/DescriptionDisplay.valaindex 7a3034c..21e758a 100644--- a/libbirdfont/DescriptionDisplay.vala+++ b/libbirdfont/DescriptionDisplay.vala@@ -213,16 +213,22 @@ public class DescriptionDisplay : FontDisplay { 			case 'a': 				if (KeyBindings.has_ctrl ()) { 					focus.select_all ();+				} else {+					insert_character (keyval); 				} 				break; 			case 'c': 				if (KeyBindings.has_ctrl ()) { 					ClipTool.copy_text (focus);+				} else {+					insert_character (keyval); 				} 				break; 			case 'v': 				if (KeyBindings.has_ctrl ()) { 					ClipTool.paste_text (focus);+				} else {+					insert_character (keyval); 				} 				break; 			case Key.RIGHT:@@ -249,18 +255,32 @@ public class DescriptionDisplay : FontDisplay { 				} 				break;		 			default:-				if (!is_modifier_key (keyval)) {-					s = (!) c.to_string ();-					if (s.validate () && keyboard_focus != null) {-						focus.insert_text (s);-					}-				}+				insert_character (keyval); 				break; 		} 		 		GlyphCanvas.redraw (); 	} +	public void insert_character (uint keyval) {+		unichar c = (unichar) keyval;+		string s;+		TextArea focus;+		+		if (keyboard_focus == null) {+			return;+		}+		+		focus = (!) keyboard_focus;+				+		if (!is_modifier_key (keyval)) {+			s = (!) c.to_string ();+			if (s.validate () && keyboard_focus != null) {+				focus.insert_text (s);+			}+		}+	}+ 	public override void button_press (uint button, double x, double y) { 		TextArea t; 		TextArea old;diff --git a/libbirdfont/Renderer/Text.vala b/libbirdfont/Renderer/Text.valaindex cb0a9d8..887244d 100644--- a/libbirdfont/Renderer/Text.vala+++ b/libbirdfont/Renderer/Text.vala@@ -381,6 +381,10 @@ public class Text : Widget { 		} 	} +	public double get_baseline_to_bottom () {+		return get_scale () * (font.base_line - font.bottom_limit);+	}+ 	public double get_scale () { 		return font_size / (font.top_limit - font.bottom_limit); 	}diff --git a/libbirdfont/Renderer/TextArea.vala b/libbirdfont/Renderer/TextArea.valaindex 19e9698..d41aed3 100644--- a/libbirdfont/Renderer/TextArea.vala+++ b/libbirdfont/Renderer/TextArea.vala@@ -31,7 +31,7 @@ public class TextArea : Widget { 			carret_is_visible = value;  			if (!value) { 				update_selection = false;-				selection_end = carret;+				selection_end = carret.character_index; 			} 		} 	}@@ -41,22 +41,20 @@ public class TextArea : Widget { 	public double width; 	public double height; -	int carret = 0;+	Carret carret = new Carret (); 	int selection_end = -1; 	bool update_selection = false; 	-	string text = "";-	int text_length = 0;-		-	int iter_pos;-	int last_paragraph = 0;-	 	public signal void text_changed (string text); 	Gee.ArrayList<Paragraph> paragraphs = new Gee.ArrayList<Paragraph>  (); 	private static const int DONE = -2;  	 	int64 cache_id = -1; 	+	int last_paragraph = 0;+	string text;+	int text_length;+	 	public TextArea (double font_size = 20) { 		this.font_size = font_size; 		width = min_width;@@ -81,6 +79,7 @@ public class TextArea : Widget { 		int next_paragraph = -1; 		 		if (last_paragraph == DONE) {+			print ("DONE\n"); 			return; 		} 	@@ -107,14 +106,18 @@ public class TextArea : Widget { 			text = t; 		} 		-		tl = t.length;-		carret = tl;+		tl = t.length;		 		text_length += tl; 		 		paragraphs.clear (); 		generate_paragraphs (); 		-		text_changed (text);+		return_if_fail (paragraphs.size != 0);+		+		carret.paragraph = paragraphs.size - 1;+		carret.character_index = paragraphs.get (paragraphs.size - 1).text.length;+		+		text_changed (get_text ()); 	} 	 	public string get_selected_text () {@@ -124,23 +127,26 @@ public class TextArea : Widget { 			return "".dup (); 		} 		-		start = (carret < selection_end) ? carret : selection_end;-		stop = (carret > selection_end) ? carret : selection_end;+		+		//FIXME:+		start = (carret.character_index < selection_end) ? carret.character_index : selection_end;+		stop = (carret.character_index> selection_end) ? carret.character_index : selection_end; 		 		return text.substring (start, stop - start); 	} 	 	public void select_all () {-		carret = 0;+		carret.paragraph = 0;+		carret.character_index = 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;+		int start = (carret.character_index < selection_end) ? carret.character_index : selection_end;+		int stop = (carret.character_index > selection_end) ? carret.character_index : selection_end; 		set_text (text.substring (0, start) + text.substring (stop)); 		selection_end = -1;-		carret = start;+		carret.character_index = start; 	} 	 	public void remove_last_character () {@@ -148,27 +154,29 @@ public class TextArea : Widget { 		int index = 0; 		unichar c; 		-		while (text.get_next_char (ref index, out c) && index < carret) {+		while (text.get_next_char (ref index, out c) && index < carret.character_index) { 			last_index = index; 		} 		-		set_text (text.substring (0, last_index) + text.substring (carret));+		set_text (text.substring (0, last_index) + text.substring (carret.character_index)); 		selection_end = -1;-		carret = last_index;+		carret.character_index = last_index; 	} 	 	public void remove_next_character () {-		int index = carret;+		int index = carret.character_index; 		unichar c;		 		text.get_next_char (ref index, out c);-		set_text (text.substring (0, carret) + text.substring (index));+		set_text (text.substring (0, carret.character_index) + text.substring (index)); 	} 	 	public void move_carret_next () {+	//FIXME:+/* 		int index = 0; 		int index_space; 		unichar c;-		char* s = (char*) text + carret;+		char* s = (char*) text + carret.character_index; 		string n = (string) s;  		if (!has_selection () && KeyBindings.has_shift ()) {@@ -187,20 +195,27 @@ public class TextArea : Widget { 		} 		 		carret += index;+		*/ 	}  	public void move_carret_previous () {+	//FIXME:+/* 		int last_index = 0; 		int index = 0; 		unichar c; 		int index_space; 		-		while (text.get_next_char (ref index, out c) && index < carret) {+		if (carret.character_index == 0) {+			carret.paragraph--;+		}+		+		while (text.get_next_char (ref index, out c) && index < carret.character_index) { 			last_index = index; 		} 		 		if (!has_selection () && KeyBindings.has_shift ()) {-			selection_end = carret;+			//FIXME: selection_end = carret; 		} else if (!KeyBindings.has_shift ()) { 			selection_end = -1; 		}@@ -212,15 +227,18 @@ public class TextArea : Widget { 			} 		} 				-		carret = last_index;+		carret.character_index = last_index;+		*/ 	} 	 	public bool has_selection () {-		return selection_end >= 0 && selection_end != carret;+		return false;+		// FIXME: return selection_end >= 0 && selection_end != carret; 	} 		 	public void insert_text (string t) { 		string s;+		Paragraph paragraph; 		 		if (single_line) { 			s = t.replace ("\n", "").replace ("\r", "");@@ -231,36 +249,122 @@ public class TextArea : Widget { 		if (has_selection ()) { 			delete_selected_text (); 		}--		string nt = text.substring (0, carret);+		+		return_if_fail (0 <= carret.paragraph < paragraphs.size);+		paragraph = paragraphs.get (carret.paragraph);+		+		string nt = paragraph.text.substring (0, carret.character_index); 		int tl = s.length; 		nt += s;-		nt += text.substring (carret);-		carret += tl;-		text = nt;-		text_length += tl;+		nt += paragraph.text.substring (carret.character_index);+		carret.character_index += tl;+	+		paragraph.set_text (nt);+		layout ();+		+		text_changed (get_text ());+	}+	+	public string get_text () {+		StringBuilder sb = new StringBuilder ();+		foreach (Paragraph p in paragraphs) {+			sb.append (p.text);+		}+		return sb.str;+	}+	+	Carret get_carret_at (double click_x, double click_y) {+		int i = 0;+		double tx, ty;+		double p;+		string w;+		int ch_index;+		double min_d = double.MAX;+		Carret c = new Carret ();+		double dt;+		+		c.paragraph = -1;+		+		foreach (Paragraph paragraph in paragraphs) {+			if (paragraph.text_is_on_screen (allocation, widget_y)) {+				ch_index = 0;+				+				tx = paragraph.start_x;+				ty = paragraph.start_y + widget_y;+				+				foreach (Text next_word in paragraph.words) {+					w = next_word.text;+					+					next_word.set_source_rgba (0, 0, 0, 1);+					+					p = next_word.get_sidebearing_extent ();++					if (ty - next_word.font_size + next_word.get_baseline_to_bottom () <= click_y <= ty + next_word.get_baseline_to_bottom ()) {+						next_word.iterate ((glyph, kerning, last) => {+							double cw;+							int ci;+							double d;+							+							cw = (glyph.get_width ()) * next_word.get_scale () + kerning;+							ci = ((!) glyph.get_unichar ().to_string ()).length;+							+							d = Math.fabs (click_x - (tx + widget_x + cw + padding));+							if (d < min_d) {+								min_d = d;+								c.character_index = ch_index + ci;+								c.paragraph = i;+							}+							+							tx += cw;+							ch_index += ci;+						});+					} else {+						if (tx + p > width || w == "\n") {+							tx = 0;+							ty += next_word.font_size;+						}+											+						if (w != "\n") {+							tx += p;+						}+						+						ch_index += w.length;+					}+				}				+			}+			i++;+		} 		-		paragraphs.clear (); // DELETE-		generate_paragraphs (); // FIXME:+		if (unlikely (c.paragraph < 0)) {+			warning (@"No carret");+			return new Carret ();+		} 		-		text_changed (text);+		return c; 	} 	 	/** @return offset to click in text. */-	public int layout (double click_x = -1, double click_y = -1) {+	public void layout () { 		double p; 		double tx, ty; 		string w; 		double xmax = 0; 		double width = this.width - 2 * padding;-		int carret_position = text_length;+		int carret_paragraph = 0;+		int carret_position = 0;+		int i = 0;+		+		if (paragraphs.size > 0) {+			carret_position = paragraphs.get (carret_paragraph).text.length;+			carret_paragraph = paragraphs.size - 1;+		} 		 		tx = 0; 		ty = font_size;-		iter_pos = 0;-										+ 		foreach (Paragraph paragraph in paragraphs) { 			if (paragraph.need_layout) {+ 				paragraph.start_y = ty; 				paragraph.start_x = tx; 				@@ -278,11 +382,6 @@ public class TextArea : Widget { 							ty += next_word.font_size; 						} 					}-					-					if (widget_y + ty - font_size <= click_y <= widget_y + ty + padding-						&& widget_x + tx <= click_x) {-						carret_position = find_carret_pos_in_word (next_word, iter_pos, tx, click_x);-					} 												 					if (w != "\n") { 						tx += p;@@ -301,6 +400,7 @@ public class TextArea : Widget { 									 			tx = paragraph.end_x; 			ty = paragraph.end_y;+			i++; 		} 		 		this.width = min_width;@@ -311,20 +411,15 @@ public class TextArea : Widget { 		 		this.height = fmax (min_height, ty + 2 * padding); -		if (click_x > 0 && click_x < widget_x + padding) {-			carret_position = 0;-		}- 		if (last_paragraph != DONE) { 			this.height = (text_length / (double) last_paragraph) * ty + 2 * padding; // estimate height 		} 		 		if (ty + widget_y < allocation.height && last_paragraph != DONE) { 			generate_paragraphs ();-			return layout (click_x, click_y);+			layout ();+			return; 		}-		-		return carret_position; 	} 	 	private int find_carret_pos_in_word (Text word, int iter_pos, double tx, double click_x) {@@ -361,8 +456,8 @@ public class TextArea : Widget { 	 	public void button_press (uint button, double x, double y) { 		if (is_over (x, y)) {-			carret = layout (x, y);-			selection_end = carret;+			carret = get_carret_at (x, y);+			selection_end = carret.character_index; 			update_selection = true; 		} 	}@@ -373,7 +468,7 @@ public class TextArea : Widget { 	 	public bool motion (double x, double y) { 		if (update_selection) {-			selection_end = layout (x, y);+			// FIXME selection_end = layout (x, y); 		} 		 		return update_selection;@@ -384,13 +479,14 @@ public class TextArea : Widget { 		double p; 		double tx, ty; 		string w;-		bool carret_at_end_of_word; 		double scale; 		double width; 		double x = widget_x; 		double y = widget_y; 		int selection_start, selection_stop; 		unichar c;+		double carret_x;+		double carret_y; 		 		layout (); @@ -422,10 +518,6 @@ public class TextArea : Widget { 		scale = word.get_scale (); 		y += font_size; 		-		if (draw_carret && carret == 0) {-			draw_carret_at (cr, x + padding, y + padding - 1);-		}-		 		// draw selection background 		// FIXME: put back 		/*@@ -526,14 +618,14 @@ public class TextArea : Widget { 			t.set_source_rgba (0, 0, 0, 1); 			cache_id = t.get_cache_id (); 		}-				-		for (int i = first_visible; i < last_visible; i++) { +		+		for (int i = first_visible; i < last_visible; i++) { 			paragraph = paragraphs.get (i); 			 			tx = paragraph.start_x; 			ty = paragraph.start_y; -			if (paragraph.cached_surface == null) {		+			if (paragraph.cached_surface == null) {				 				paragraph.cached_surface = new Surface.similar (cr.get_target (), Cairo.Content.COLOR_ALPHA, paragraph.get_width (), paragraph.get_height () + (int) font_size + 1); 				cc = new Context ((!) paragraph.cached_surface); 		@@ -567,41 +659,79 @@ public class TextArea : Widget { 			} 		} 		-/*-		// draw characters-		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;+		get_carret_position (out carret_x, out carret_y);+		draw_carret_at (cr, carret_x, carret_y);+	}+	+	void get_carret_position (out double carret_x, out double carret_y) {+		Paragraph paragraph;+		+		carret_x = 0;+		carret_y = 0;+		+		return_if_fail (0 <= carret.paragraph <= paragraphs.size);+		paragraph = paragraphs.get (carret.paragraph);++		double tx = paragraph.start_x;+		double ty = paragraph.start_y + widget_y;+		int ch_index = 0;+		int wl;+		double pos_x, pos_y;+		+		pos_x = -1;+		pos_y = -1;+		+		foreach (Text next_word in paragraph.words) {+			string w = next_word.text;+			double p = next_word.get_sidebearing_extent ();+			wl = w.length;++			if (carret.character_index >= ch_index + wl) {+				pos_x = tx + next_word.get_sidebearing_extent () + padding + widget_x;+				pos_y = ty + next_word.get_baseline_to_bottom ();		+			} else if (ch_index <= carret.character_index <= ch_index + wl) {+				+				if (carret.character_index <= ch_index) {+					pos_x = padding + widget_x;+					pos_y = ty + next_word.get_baseline_to_bottom ();		+				}++				next_word.iterate ((glyph, kerning, last) => {+					double cw;+					int ci;+					+					cw = (glyph.get_width ()) * next_word.get_scale () + kerning;+					ci = ((!) glyph.get_unichar ().to_string ()).length;+					+					if (ch_index == carret.character_index) {+						pos_x = tx + padding + widget_x;+						pos_y = ty + next_word.get_baseline_to_bottom ();						+					}+					+					if (ch_index < carret.character_index <= ch_index + ci) {+						pos_x = tx + padding + widget_x + cw;+						pos_y = ty + next_word.get_baseline_to_bottom ();+					}+					+					tx += cw;+					ch_index += ci;+				}); 			} 			-			word.set_text (w);-			p = word.get_sidebearing_extent ();-			 			if (tx + p > width || w == "\n") { 				tx = 0;-				ty += font_size;+				ty += next_word.font_size; 			} 			 			if (w != "\n") {-				word.draw_at_baseline (cr, x + tx, y + ty); 				tx += p; 			}--			if (y + ty > allocation.height) {-				break;-			}-							-			if (carret_at_end_of_word && draw_carret) {-				draw_carret_at (cr, x + tx, y + ty + scale * - word.font.bottom_limit);-			}+			+			ch_index += wl; 		}-		cr.restore ();-		*/+		+		carret_x = pos_x;+		carret_y = pos_y; 	} 	 	void draw_carret_at (Context cr, double x, double y) {@@ -613,77 +743,7 @@ public class TextArea : Widget { 		cr.stroke (); 		cr.restore ();		 	}-	-	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;-		string n;-		int nl;-		-		carret_at_end_of_word = false;-		-		if (iter_pos >= text_length) {-			carret_at_end_of_word = true;-			return "".dup ();-		}-		-		if (text.get_char (iter_pos) == '\n') {-			iter_pos += "\n".length;-			carret_at_end_of_word = (iter_pos == carret);-			return "\n".dup ();-		}-		-		i = text.index_of (" ", iter_pos);-		pi = i + " ".length;-		-		ni = text.index_of ("\t", iter_pos);-		if (ni != -1 && ni < pi || i == -1) {-			i = ni;-			pi = i  + "\t".length;-		}-		-		ni = text.index_of ("\n", iter_pos);-		if (ni != -1 && ni < pi || i == -1) {-			i = ni;-			pi = i;-		}-		-		if (iter_pos + iter_pos - pi > text_length || i == -1) {-			n = text.substring (iter_pos);-		} else {-			n = text.substring (iter_pos, pi - iter_pos);-		} 		-		nl = n.length;-		if (iter_pos < carret < iter_pos + nl) {-			n = text.substring (iter_pos, carret - iter_pos);-			nl = n.length;-			carret_at_end_of_word = true;-		}-		-		iter_pos += nl;-		-		if (iter_pos == carret) {-			carret_at_end_of_word = true;-		}-		-		return n;-	}-	 	class Paragraph : GLib.Object { 		public double end_x = -10000; 		public double end_y = -10000;@@ -702,10 +762,20 @@ public class TextArea : Widget { 		 		public Surface? cached_surface = null; 		+		double font_size;+		 		public Paragraph (string text, double font_size) {-			this.text = text;-			text_length = text.length;-			generate_words (font_size);+			this.font_size = font_size;+			set_text (text);+		}++		public void set_text (string t) {+			this.text = t;+			text_length = t.length;+			need_layout = true;+			words.clear ();+			cached_surface = null;+			generate_words (); 		}  		public int get_height () {@@ -723,7 +793,7 @@ public class TextArea : Widget { 			return v; 		} -		void generate_words (double font_size) {+		void generate_words () { 			string w; 			int p = 0; 			bool carret_at_word_end = false;@@ -800,6 +870,19 @@ public class TextArea : Widget { 			return n; 		} 	}+	+	public class Carret : GLib.Object {+		+		public int paragraph = 0;+		public int character_index = 0;+		+		public Carret () {+		}+		+		public void print () {+			stdout.printf (@"paragraph: $paragraph, character_index: $character_index\n");+		}+	} }  }

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112