diff --git a/Terminal.Gui/Event.cs b/Terminal.Gui/Event.cs index a0ce2ef6a..8556a370b 100644 --- a/Terminal.Gui/Event.cs +++ b/Terminal.Gui/Event.cs @@ -38,6 +38,11 @@ namespace Terminal.Gui { /// SpecialMask = 0xfff00000, + /// + /// The key code for the user pressing Control-spacebar + /// + ControlSpace = 0, + /// /// The key code for the user pressing Control-A /// diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 6a4df6661..0eac41b70 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -7,8 +7,6 @@ // // TODO: // Attributed text on spans -// Render selection -// Mark/Delete/Cut commands // Replace insertion with Insert method // String accumulation (Control-k, control-k is not preserving the last new line, see StringToRunes // @@ -63,7 +61,7 @@ namespace Terminal.Gui { start = i + 1; } } - if (i - start > 0) + if (i - start >= 0) lines.Add (ToRunes (content [start, null])); return lines; } @@ -138,8 +136,11 @@ namespace Terminal.Gui { int leftColumn; int currentRow; int currentColumn; - bool used; + int selectionStartColumn, selectionStartRow; + bool selecting; + //bool used; +#if false /// /// Changed event, raised when the text has clicked. /// @@ -148,7 +149,7 @@ namespace Terminal.Gui { /// raised when the text in the entry changes. /// public event EventHandler Changed; - +#endif /// /// Public constructor. /// @@ -197,6 +198,12 @@ namespace Terminal.Gui { /// public override void PositionCursor () { + if (selecting) { + var minRow = Math.Min (Math.Max (Math.Min (selectionStartRow, currentRow)-topRow, 0), Frame.Height); + var maxRow = Math.Min (Math.Max (Math.Max (selectionStartRow, currentRow) - topRow, 0), Frame.Height); + + SetNeedsDisplay (new Rect (0, minRow, Frame.Width, maxRow)); + } Move (CurrentColumn - leftColumn, CurrentRow - topRow); } @@ -209,20 +216,117 @@ namespace Terminal.Gui { } } + void ColorNormal () + { + Driver.SetAttribute (ColorScheme.Normal); + } + + void ColorSelection () + { + if (HasFocus) + Driver.SetAttribute (ColorScheme.Focus); + else + Driver.SetAttribute (ColorScheme.Normal); + } + + // Returns an encoded region start..end (top 32 bits are the row, low32 the column) + void GetEncodedRegionBounds (out long start, out long end) + { + long selection = ((long)(uint)selectionStartRow << 32) | (uint)selectionStartColumn; + long point = ((long)(uint)currentRow << 32) | (uint)currentColumn; + if (selection > point) { + start = point; + end = selection; + } else { + start = selection; + end = point; + } + } + + bool PointInSelection (int col, int row) + { + long start, end; + GetEncodedRegionBounds (out start, out end); + var q = ((long)(uint)row << 32) | (uint)col; + return q >= start && q <= end; + } + + // + // Returns a ustring with the text in the selected + // region. + // + public ustring GetRegion () + { + long start, end; + GetEncodedRegionBounds (out start, out end); + int startRow = (int)(start >> 32); + var maxrow = ((int)(end >> 32)); + int startCol = (int)(start & 0xffffffff); + var endCol = (int)(end & 0xffffffff); + var line = model.GetLine (startRow); + + if (startRow == maxrow) + return StringFromRunes (line.GetRange (startCol, endCol)); + + ustring res = StringFromRunes (line.GetRange (startCol, line.Count - startCol)); + + for (int row = startRow+1; row < maxrow; row++) { + res = res + ustring.Make (10) + StringFromRunes (model.GetLine (row)); + } + line = model.GetLine (maxrow); + res = res + ustring.Make (10) + StringFromRunes (line.GetRange (0, endCol)); + return res; + } + + // + // Clears the contents of the selected region + // + public void ClearRegion () + { + long start, end; + long currentEncoded = ((long)(uint)currentRow << 32) | (uint)currentColumn; + GetEncodedRegionBounds (out start, out end); + int startRow = (int)(start >> 32); + var maxrow = ((int)(end >> 32)); + int startCol = (int)(start & 0xffffffff); + var endCol = (int)(end & 0xffffffff); + var line = model.GetLine (startRow); + + if (startRow == maxrow) { + line.RemoveRange (startCol, endCol - startCol); + currentColumn = startCol; + SetNeedsDisplay (new Rect (0, startRow - topRow, Frame.Width, startRow - topRow + 1)); + return; + } + + line.RemoveRange (startCol, line.Count - startCol); + var line2 = model.GetLine (maxrow); + line.AddRange (line2.Skip (endCol)); + for (int row = startRow + 1; row <= maxrow; row++) { + model.RemoveLine (startRow+1); + } + if (currentEncoded == end) { + currentRow -= maxrow - (startRow); + } + currentColumn = startCol; + + SetNeedsDisplay (); + } + /// /// Redraw the text editor region /// /// The region to redraw. public override void Redraw (Rect region) { - Driver.SetAttribute (ColorScheme.Focus); - Move (0, 0); + ColorNormal (); int bottom = region.Bottom; int right = region.Right; for (int row = region.Top; row < bottom; row++) { int textLine = topRow + row; if (textLine >= model.Count) { + ColorNormal (); ClearRegion (region.Left, row, region.Right, row + 1); continue; } @@ -237,6 +341,11 @@ namespace Terminal.Gui { for (int col = region.Left; col < right; col++) { var lineCol = leftColumn + col; var rune = lineCol >= lineRuneCount ? ' ' : line [lineCol]; + if (selecting && PointInSelection (col, row)) + ColorSelection (); + else + ColorNormal (); + AddRune (col, row, rune); } } @@ -366,6 +475,8 @@ namespace Terminal.Gui { int restCount; List rest; + // Handle some state here - whether the last command was a kill + // operation and the column tracking (up/down) switch (kb.Key) { case Key.ControlN: case Key.CursorDown: @@ -381,6 +492,7 @@ namespace Terminal.Gui { break; } + // Dispatch the command. switch (kb.Key) { case Key.ControlN: case Key.CursorDown: @@ -560,6 +672,24 @@ namespace Terminal.Gui { case Key.ControlY: // Control-y, yank InsertText (Clipboard.Contents); + selecting = false; + break; + + case Key.ControlSpace: + selecting = true; + selectionStartColumn = currentColumn; + selectionStartRow = currentRow; + break; + + case (Key)((int)'w' + Key.AltMask): + SetClipboard (GetRegion ()); + selecting = false; + break; + + case Key.ControlW: + SetClipboard (GetRegion ()); + ClearRegion (); + selecting = false; break; case (Key)((int)'b' + Key.AltMask): @@ -690,7 +820,7 @@ namespace Terminal.Gui { SetNeedsDisplay (); return true; } - #endif +#endif } }