Example #1
0
		/// <summary>
		/// Go through all tags on a line and recalculate all size-related values;
		/// returns true if lineheight changed
		/// </summary>
		internal bool RecalculateLine (Graphics g, Document doc)
		{
			LineTag tag;
			int pos;
			int len;
			SizeF size;
			float w;
			int prev_offset;
			bool retval;
			bool wrapped;
			Line line;
			int wrap_pos;
			int prev_height;
			int prev_ascent;

			pos = 0;
			len = this.text.Length;
			tag = this.tags;
			prev_offset = this.offset;	// For drawing optimization calculations
			prev_height = this.height;
			prev_ascent = this.ascent;
			this.height = 0;		// Reset line height
			this.ascent = 0;		// Reset the ascent for the line
			tag.Shift = 0;			// Reset shift (which should be stored as pixels, not as points)

			if (ending == LineEnding.Wrap)
				widths[0] = document.left_margin + hanging_indent;
			else
				widths[0] = document.left_margin + indent;

			this.recalc = false;
			retval = false;
			wrapped = false;

			wrap_pos = 0;

			while (pos < len) {

				while (tag.Length == 0) {	// We should always have tags after a tag.length==0 unless len==0
					//tag.Ascent = 0;
					tag.Shift = (tag.Line.ascent - tag.Ascent) / 72;
					tag = tag.Next;
				}

				size = tag.SizeOfPosition (g, pos);
				w = size.Width;

				if (Char.IsWhiteSpace (text[pos]))
					wrap_pos = pos + 1;

				if (doc.wrap) {
					if ((wrap_pos > 0) && (wrap_pos != len) && (widths[pos] + w) + 5 > (doc.viewport_width - this.right_indent)) {
						// Make sure to set the last width of the line before wrapping
						widths[pos + 1] = widths[pos] + w;

						pos = wrap_pos;
						len = text.Length;
						doc.Split (this, tag, pos);
						ending = LineEnding.Wrap;
						len = this.text.Length;

						retval = true;
						wrapped = true;
					} else if (pos > 1 && (widths[pos] + w) > (doc.viewport_width - this.right_indent)) {
						// No suitable wrap position was found so break right in the middle of a word

						// Make sure to set the last width of the line before wrapping
						widths[pos + 1] = widths[pos] + w;

						doc.Split (this, tag, pos);
						ending = LineEnding.Wrap;
						len = this.text.Length;
						retval = true;
						wrapped = true;
					}
				}

				// Contract all wrapped lines that follow back into our line
				if (!wrapped) {
					pos++;

					widths[pos] = widths[pos - 1] + w;

					if (pos == len) {
						line = doc.GetLine (this.line_no + 1);
						if ((line != null) && (ending == LineEnding.Wrap || ending == LineEnding.None)) {
							// Pull the two lines together
							doc.Combine (this.line_no, this.line_no + 1);
							len = this.text.Length;
							retval = true;
						}
					}
				}

				if (pos == (tag.Start - 1 + tag.Length)) {
					// We just found the end of our current tag
					tag.Height = tag.MaxHeight ();

					// Check if we're the tallest on the line (so far)
					if (tag.Height > this.height)
						this.height = tag.Height;	// Yep; make sure the line knows

					if (tag.Ascent > this.ascent) {
						LineTag t;

						// We have a tag that has a taller ascent than the line;
						t = tags;
						while (t != null && t != tag) {
							t.Shift = (tag.Ascent - t.Ascent) / 72;
							t = t.Next;
						}

						// Save on our line
						this.ascent = tag.Ascent;
					} else {
						tag.Shift = (this.ascent - tag.Ascent) / 72;
					}

					tag = tag.Next;
					if (tag != null) {
						tag.Shift = 0;
						wrap_pos = pos;
					}
				}
			}

			while (tag != null) {	
				tag.Shift = (tag.Line.ascent - tag.Ascent) / 72;
				tag = tag.Next;
			}

			if (this.height == 0) {
				this.height = tags.Font.Height;
				tags.Height = this.height;
				tags.Shift = 0;
			}

			if (prev_offset != offset || prev_height != this.height || prev_ascent != this.ascent)
				retval = true;

			return retval;
		}
Example #2
0
		private bool RecalculateLine (Graphics g, Document doc, bool handleKerning)
		{
			LineTag tag;
			int pos;
			int len;
			SizeF size;
			float w;
			int prev_offset;
			bool retval;
			bool wrapped;
			Line line;
			int wrap_pos;
			int prev_height;
			int prev_ascent;

			pos = 0;
			len = this.text.Length;
			tag = this.tags;
			prev_offset = this.offset;	// For drawing optimization calculations
			prev_height = this.height;
			prev_ascent = this.ascent;
			this.height = 0;		// Reset line height
			this.ascent = 0;		// Reset the ascent for the line
			tag.Shift = 0;			// Reset shift (which should be stored as pixels, not as points)

			if (ending == LineEnding.Wrap)
				widths[0] = document.left_margin + hanging_indent;
			else
				widths[0] = document.left_margin + indent;

			this.recalc = false;
			retval = false;
			wrapped = false;

			wrap_pos = 0;

			while (pos < len) {

				while (tag.Length == 0) {	// We should always have tags after a tag.length==0 unless len==0
					//tag.Ascent = 0;
					tag.Shift = (tag.Line.ascent - tag.Ascent) / 72;
					tag = tag.Next;
				}

				// kerning is a problem.  The original code in this method assumed that the
				// width of a string equals the sum of the widths of its characters.  This is
				// not true when kerning takes place during the display process.  Since it's
				// impossible to find out easily whether a font does kerning, and with which
				// characters, we just detect that kerning must have happened and use a slower
				// (but accurate) measurement for those fonts henceforth.  Without handling
				// kerning, many fonts for English become unreadable during typing for many
				// input strings, and text in many other languages is even worse trying to
				// type in TextBoxes.
				// See https://bugzilla.xamarin.com/show_bug.cgi?id=26478 for details.
				float newWidth;
				if (handleKerning && !Char.IsWhiteSpace(text[pos]))
				{
					// MeasureText doesn't measure trailing spaces, so we do the best we can for those
					// in the else branch.
					size = TextBoxTextRenderer.MeasureText (g, text.ToString (0, pos + 1), tag.Font);
					newWidth = widths[0] + size.Width;
				}
				else
				{
					size = tag.SizeOfPosition (g, pos);
					w = size.Width;
					newWidth = widths[pos] + w;
				}

				if (Char.IsWhiteSpace (text[pos]))
					wrap_pos = pos + 1;

				if (doc.wrap) {
					if ((wrap_pos > 0) && (wrap_pos != len) && (newWidth + 5) > (doc.viewport_width - this.right_indent)) {
						// Make sure to set the last width of the line before wrapping
						widths[pos + 1] = newWidth;

						pos = wrap_pos;
						len = text.Length;
						doc.Split (this, tag, pos);
						ending = LineEnding.Wrap;
						len = this.text.Length;

						retval = true;
						wrapped = true;
					} else if (pos > 1 && newWidth > (doc.viewport_width - this.right_indent)) {
						// No suitable wrap position was found so break right in the middle of a word

						// Make sure to set the last width of the line before wrapping
						widths[pos + 1] = newWidth;

						doc.Split (this, tag, pos);
						ending = LineEnding.Wrap;
						len = this.text.Length;
						retval = true;
						wrapped = true;
					}
				}

				// Contract all wrapped lines that follow back into our line
				if (!wrapped) {
					pos++;

					widths[pos] = newWidth;

					if (pos == len) {
						line = doc.GetLine (this.line_no + 1);
						if ((line != null) && (ending == LineEnding.Wrap || ending == LineEnding.None)) {
							// Pull the two lines together
							doc.Combine (this.line_no, this.line_no + 1);
							len = this.text.Length;
							retval = true;
						}
					}
				}

				if (pos == (tag.Start - 1 + tag.Length)) {
					// We just found the end of our current tag
					tag.Height = tag.MaxHeight ();

					// Check if we're the tallest on the line (so far)
					if (tag.Height > this.height)
						this.height = tag.Height;	// Yep; make sure the line knows

					if (tag.Ascent > this.ascent) {
						LineTag t;

						// We have a tag that has a taller ascent than the line;
						t = tags;
						while (t != null && t != tag) {
							t.Shift = (tag.Ascent - t.Ascent) / 72;
							t = t.Next;
						}

						// Save on our line
						this.ascent = tag.Ascent;
					} else {
						tag.Shift = (this.ascent - tag.Ascent) / 72;
					}

					tag = tag.Next;
					if (tag != null) {
						tag.Shift = 0;
						wrap_pos = pos;
					}
				}
			}

			var fullText = text.ToString();
			if (!handleKerning && fullText.Length > 1 && !wrapped)
			{
				// Check whether kerning takes place for this string and font.
				var realSize = TextBoxTextRenderer.MeasureText(g, fullText, tags.Font);
				float realWidth = realSize.Width + widths[0];
				// MeasureText ignores trailing whitespace, so we will too at this point.
				int length = fullText.TrimEnd().Length;
				float sumWidth = widths[length];
				if (realWidth != sumWidth)
				{
					kerning_fonts.Add(tags.Font.GetHashCode (), true);
					// Using a slightly incorrect width this time around isn't that bad. All that happens
					// is that the cursor is a pixel or two off until the next character is typed.  It's
					// the accumulation of pixel after pixel that causes display problems.
				}
			}

			while (tag != null) {	
				tag.Shift = (tag.Line.ascent - tag.Ascent) / 72;
				tag = tag.Next;
			}

			if (this.height == 0) {
				this.height = tags.Font.Height;
				tags.Height = this.height;
				tags.Shift = 0;
			}

			if (prev_offset != offset || prev_height != this.height || prev_ascent != this.ascent)
				retval = true;

			return retval;
		}