diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 6ac03c572..1909bae31 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -1450,6 +1450,70 @@ namespace Terminal.Gui { OnLayoutComplete (new LayoutEventArgs () { OldBounds = oldBounds }); } + /// + /// A generic virtual method at the level of View to manipulate any hot-keys. + /// + /// The text to manipulate. + /// The hot-key to look for. + /// The returning hot-key position. + /// The character immediately to the right relative to the hot-key position + /// + public virtual ustring GetTextFromHotKey(ustring text, Rune hotKey, out int hotPos, out Rune showHotKey) + { + Rune hot_key = (Rune)0; + int hot_pos = -1; + ustring shown_text = text; + + // Use first hot_key char passed into 'hotKey'. + int i = 0; + foreach (Rune c in shown_text) { + if ((char)c != 0xFFFD) { + if (c == hotKey) { + hot_pos = i; + } else if (hot_pos > -1) { + hot_key = (char)c; + break; + } + } + i++; + } + + if (hot_pos == -1) { + // Use first upper-case char if there are no hot-key in the text. + i = 0; + foreach (Rune c in shown_text) { + if (Rune.IsUpper (c)) { + hot_key = (char)c; + hot_pos = i; + break; + } + i++; + } + } else { + // Use char after 'hotKey' + ustring start = ""; + i = 0; + foreach (Rune c in shown_text) { + start += ustring.Make (c); + i++; + if (i == hot_pos) + break; + } + var st = shown_text; + shown_text = start; + i = 0; + foreach (Rune c in st) { + i++; + if (i > hot_pos + 1) { + shown_text += ustring.Make (c); + } + } + } + hotPos = hot_pos; + showHotKey = hot_key; + return shown_text; + } + /// /// Pretty prints the View /// @@ -1505,7 +1569,6 @@ namespace Terminal.Gui { return false; } - /// /// Method invoked when a mouse event is generated /// diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index db47043d4..bff8d0a44 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -168,26 +168,7 @@ namespace Terminal.Gui { else shown_text = ustring.Make (_leftBracket) + " " + text + " " + ustring.Make (_rightBracket); - hot_key = (Rune)0; - hot_pos = shown_text.IndexOf ('_'); - - if (hot_pos == -1) { - // Use first upper-case char - int i = 0; - foreach (Rune c in shown_text) { - if (Rune.IsUpper (c)) { - hot_key = c; - hot_pos = i; - break; - } - i++; - } - } else { - // Use char after '_' - var start = shown_text [0, hot_pos]; - shown_text = start + shown_text [hot_pos + 1, shown_text.Length]; - hot_key = Char.ToUpper((char)shown_text [hot_pos]); - } + shown_text = GetTextFromHotKey (shown_text, '_', out hot_pos, out hot_key); SetNeedsDisplay (); } @@ -207,30 +188,30 @@ namespace Terminal.Gui { if (Frame.Width > shown_text.Length + 1) { switch (TextAlignment) { case TextAlignment.Left: - caption += new string (' ', Frame.Width - caption.Length); + caption += new string (' ', Frame.Width - caption.RuneCount); break; case TextAlignment.Right: - start = Frame.Width - caption.Length; - caption = $"{new string (' ', Frame.Width - caption.Length)}{caption}"; + start = Frame.Width - caption.RuneCount; + caption = $"{new string (' ', Frame.Width - caption.RuneCount)}{caption}"; if (c_hot_pos > -1) { c_hot_pos += start; } break; case TextAlignment.Centered: - start = Frame.Width / 2 - caption.Length / 2; - caption = $"{new string (' ', start)}{caption}{new string (' ', Frame.Width - caption.Length - start)}"; + start = Frame.Width / 2 - caption.RuneCount / 2; + caption = $"{new string (' ', start)}{caption}{new string (' ', Frame.Width - caption.RuneCount - start)}"; if (c_hot_pos > -1) { c_hot_pos += start; } break; case TextAlignment.Justified: - var words = caption.ToString ().Split (new string [] { " " }, StringSplitOptions.RemoveEmptyEntries); - var wLen = GetWordsLength (words); - var space = (Frame.Width - wLen) / (caption.Length - wLen); + var words = caption.Split (" "); + var wLen = GetWordsLength (words, out int runeCount); + var space = (Frame.Width - runeCount) / (caption.Length - wLen); caption = ""; for (int i = 0; i < words.Length; i++) { if (i == words.Length - 1) { - caption += new string (' ', Frame.Width - caption.Length - 1); + caption += new string (' ', Frame.Width - caption.RuneCount - 1); caption += words [i]; } else { caption += words [i]; @@ -240,7 +221,7 @@ namespace Terminal.Gui { } } if (c_hot_pos > -1) { - c_hot_pos += space - 1; + c_hot_pos += space - 1 + (wLen - runeCount == 0 ? 0 : wLen - runeCount + 1); } break; } @@ -255,14 +236,15 @@ namespace Terminal.Gui { } } - int GetWordsLength (string[] words) + int GetWordsLength (ustring [] words, out int runeCount) { int length = 0; - + int rCount = 0; for (int i = 0; i < words.Length; i++) { length += words [i].Length; + rCount += words [i].RuneCount; } - + runeCount = rCount; return length; } @@ -274,7 +256,7 @@ namespace Terminal.Gui { bool CheckKey (KeyEvent key) { - if (Char.ToUpper ((char)key.KeyValue) == hot_key) { + if ((char)key.KeyValue == hot_key) { this.SuperView.SetFocus (this); Clicked?.Invoke (); return true; diff --git a/UICatalog/Scenarios/Buttons.cs b/UICatalog/Scenarios/Buttons.cs index 08c219d16..e6e4bff6c 100644 --- a/UICatalog/Scenarios/Buttons.cs +++ b/UICatalog/Scenarios/Buttons.cs @@ -154,7 +154,7 @@ namespace UICatalog { absoluteFrame.Add (moveBtnA); // Demonstrates how changing the View.Frame property can SIZE Views (#583) - var sizeBtnA = new Button (0, 2, "Size This Button via Frame") { + var sizeBtnA = new Button (0, 2, " ~  s  gui.cs   master ↑10 = Со_хранить") { ColorScheme = Colors.Error, }; sizeBtnA.Clicked = () => { @@ -233,6 +233,16 @@ namespace UICatalog { moveHotKeyBtn.Text = MoveHotkey (moveHotKeyBtn.Text); }; Win.Add (moveHotKeyBtn); + + var moveUnicodeHotKeyBtn = new Button (" ~  s  gui.cs   master ↑10 = Сохранить") { + X = Pos.Right (moveHotKeyBtn) + 6, + Y = Pos.Bottom (radioGroup) + 1, + ColorScheme = Colors.TopLevel, + }; + moveUnicodeHotKeyBtn.Clicked = () => { + moveUnicodeHotKeyBtn.Text = MoveHotkey (moveUnicodeHotKeyBtn.Text); + }; + Win.Add (moveUnicodeHotKeyBtn); } } }