From d40fed5ce2bc806a91245adb18039634eac13ed0 Mon Sep 17 00:00:00 2001 From: MichaelTheShifter Date: Wed, 20 Jul 2016 09:40:36 -0400 Subject: Move ShiftUI source code to ShiftOS This'll be a lot easier to work on. --- source/ShiftUI/Widgets/TextBox.cs | 1025 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1025 insertions(+) create mode 100644 source/ShiftUI/Widgets/TextBox.cs (limited to 'source/ShiftUI/Widgets/TextBox.cs') diff --git a/source/ShiftUI/Widgets/TextBox.cs b/source/ShiftUI/Widgets/TextBox.cs new file mode 100644 index 0000000..1ef880d --- /dev/null +++ b/source/ShiftUI/Widgets/TextBox.cs @@ -0,0 +1,1025 @@ +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2004-2006 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok pbartok@novell.com +// Daniel Nauck (dna(at)mono-project(dot)de) +// + +// NOT COMPLETE + +using System; +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace ShiftUI { + + [ComVisible(true)] + [ClassInterface (ClassInterfaceType.AutoDispatch)] + //[Designer ("ShiftUI.Design.TextBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")] + [ToolboxWidget] + public class TextBox : TextBoxBase { + #region Variables + private MenuItem undo; + private MenuItem cut; + private MenuItem copy; + private MenuItem paste; + private MenuItem delete; + private MenuItem select_all; + + private bool use_system_password_char; + private AutoCompleteStringCollection auto_complete_custom_source; + private AutoCompleteMode auto_complete_mode = AutoCompleteMode.None; + private AutoCompleteSource auto_complete_source = AutoCompleteSource.None; + private AutoCompleteListBox auto_complete_listbox; + private string auto_complete_original_text; + private int auto_complete_selected_index = -1; + private List auto_complete_matches; + private ComboBox auto_complete_cb_source; + #endregion // Variables + + #region Public Constructors + public TextBox() { + + scrollbars = RichTextBoxScrollBars.None; + alignment = HorizontalAlignment.Left; + this.LostFocus +=new EventHandler(TextBox_LostFocus); + this.RightToLeftChanged += new EventHandler (TextBox_RightToLeftChanged); + MouseWheel += new MouseEventHandler (TextBox_MouseWheel); + + BackColor = ThemeEngine.Current.ColorControl; + ForeColor = ThemeEngine.Current.ColorControlText; + backcolor_set = false; + + SetStyle (Widgetstyles.StandardClick | Widgetstyles.StandardDoubleClick, false); + SetStyle (Widgetstyles.FixedHeight, true); + + + document.multiline = false; + } + + #endregion // Public Constructors + + #region Private & Internal Methods + + void TextBox_RightToLeftChanged (object sender, EventArgs e) + { + UpdateAlignment (); + } + + private void TextBox_LostFocus (object sender, EventArgs e) { + if (hide_selection) + document.InvalidateSelectionArea (); + if (auto_complete_listbox != null && auto_complete_listbox.Visible) + auto_complete_listbox.HideListBox (false); + } + + private void TextBox_MouseWheel (object o, MouseEventArgs args) + { + if (auto_complete_listbox == null || !auto_complete_listbox.Visible) + return; + + int lines = args.Delta / 120; + auto_complete_listbox.Scroll (-lines); + } + + // Receives either WM_KEYDOWN or WM_CHAR that will likely need the generation/lookup + // of new matches + private void ProcessAutoCompleteInput (ref Message m, bool deleting_chars) + { + // Need to call base.WndProc before to have access to + // the updated Text property value + base.WndProc (ref m); + auto_complete_original_text = Text; + ShowAutoCompleteListBox (deleting_chars); + } + + private void ShowAutoCompleteListBox (bool deleting_chars) + { + // + // We only support CustomSource by now + // + + IList source = auto_complete_cb_source == null ? auto_complete_custom_source : (IList)auto_complete_cb_source.Items; + + bool append = auto_complete_mode == AutoCompleteMode.Append || auto_complete_mode == AutoCompleteMode.SuggestAppend; + bool suggest = auto_complete_mode == AutoCompleteMode.Suggest || auto_complete_mode == AutoCompleteMode.SuggestAppend; + + if (Text.Length == 0) { + if (auto_complete_listbox != null) + auto_complete_listbox.HideListBox (false); + return; + } + + if (auto_complete_matches == null) + auto_complete_matches = new List (); + + string text = Text; + auto_complete_matches.Clear (); + + for (int i = 0; i < source.Count; i++) { + string item_text = auto_complete_cb_source == null ? auto_complete_custom_source [i] : + auto_complete_cb_source.GetItemText (auto_complete_cb_source.Items [i]); + if (item_text.StartsWith (text, StringComparison.CurrentCultureIgnoreCase)) + auto_complete_matches.Add (item_text); + } + + auto_complete_matches.Sort (); + + // Return if we have a single exact match + if ((auto_complete_matches.Count == 0) || (auto_complete_matches.Count == 1 && + auto_complete_matches [0].Equals (text, StringComparison.CurrentCultureIgnoreCase))) { + + if (auto_complete_listbox != null && auto_complete_listbox.Visible) + auto_complete_listbox.HideListBox (false); + return; + } + + auto_complete_selected_index = suggest ? -1 : 0; + + if (suggest) { + if (auto_complete_listbox == null) + auto_complete_listbox = new AutoCompleteListBox (this); + + // Show or update auto complete listbox contents + auto_complete_listbox.Location = PointToScreen (new Point (0, Height)); + auto_complete_listbox.ShowListBox (); + } + + if (append && !deleting_chars) + AppendAutoCompleteMatch (0); + + document.MoveCaret (CaretDirection.End); + } + + internal void HideAutoCompleteList () + { + if (auto_complete_listbox != null) + auto_complete_listbox.HideListBox (false); + } + + internal bool IsAutoCompleteAvailable { + get { + if (auto_complete_source == AutoCompleteSource.None || auto_complete_mode == AutoCompleteMode.None) + return false; + + // We only support CustomSource by now, as well as an internal custom source used by ComboBox + if (auto_complete_source != AutoCompleteSource.CustomSource) + return false; + IList custom_source = auto_complete_cb_source == null ? auto_complete_custom_source : (IList)auto_complete_cb_source.Items; + if (custom_source == null || custom_source.Count == 0) + return false; + + return true; + } + } + + internal ComboBox AutoCompleteInternalSource { + get { + return auto_complete_cb_source; + } + set { + auto_complete_cb_source = value; + } + } + + internal bool CanNavigateAutoCompleteList { + get { + if (auto_complete_mode == AutoCompleteMode.None) + return false; + if (auto_complete_matches == null || auto_complete_matches.Count == 0) + return false; + + bool suggest_window_visible = auto_complete_listbox != null && auto_complete_listbox.Visible; + if (auto_complete_mode == AutoCompleteMode.Suggest && !suggest_window_visible) + return false; + + return true; + } + } + + bool NavigateAutoCompleteList (Keys key) + { + if (auto_complete_matches == null || auto_complete_matches.Count == 0) + return false; + + bool suggest_window_visible = auto_complete_listbox != null && auto_complete_listbox.Visible; + if (!suggest_window_visible && auto_complete_mode == AutoCompleteMode.Suggest) + return false; + + int index = auto_complete_selected_index; + + switch (key) { + case Keys.Up: + index -= 1; + if (index < -1) + index = auto_complete_matches.Count - 1; + break; + case Keys.Down: + index += 1; + if (index >= auto_complete_matches.Count) + index = -1; + break; + case Keys.PageUp: + if (auto_complete_mode == AutoCompleteMode.Append || !suggest_window_visible) + goto case Keys.Up; + + if (index == -1) + index = auto_complete_matches.Count - 1; + else if (index == 0) + index = -1; + else { + index -= auto_complete_listbox.page_size - 1; + if (index < 0) + index = 0; + } + break; + case Keys.PageDown: + if (auto_complete_mode == AutoCompleteMode.Append || !suggest_window_visible) + goto case Keys.Down; + + if (index == -1) + index = 0; + else if (index == auto_complete_matches.Count - 1) + index = -1; + else { + index += auto_complete_listbox.page_size - 1; + if (index >= auto_complete_matches.Count) + index = auto_complete_matches.Count - 1; + } + break; + default: + break; + } + + // In SuggestAppend mode the navigation mode depends on the visibility of the suggest lb. + bool suggest = auto_complete_mode == AutoCompleteMode.Suggest || auto_complete_mode == AutoCompleteMode.SuggestAppend; + if (suggest && suggest_window_visible) { + Text = index == -1 ? auto_complete_original_text : auto_complete_matches [index]; + auto_complete_listbox.HighlightedIndex = index; + } else + // Append only, not suggest at all + AppendAutoCompleteMatch (index < 0 ? 0 : index); + + auto_complete_selected_index = index; + document.MoveCaret (CaretDirection.End); + + return true; + } + + void AppendAutoCompleteMatch (int index) + { + Text = auto_complete_original_text + auto_complete_matches [index].Substring (auto_complete_original_text.Length); + SelectionStart = auto_complete_original_text.Length; + SelectionLength = auto_complete_matches [index].Length - auto_complete_original_text.Length; + } + + // this is called when the user selects a value from the autocomplete list + // *with* the mouse + internal virtual void OnAutoCompleteValueSelected (EventArgs args) + { + } + + private void UpdateAlignment () + { + HorizontalAlignment new_alignment = alignment; + RightToLeft rtol = GetInheritedRtoL (); + + if (rtol == RightToLeft.Yes) { + if (new_alignment == HorizontalAlignment.Left) + new_alignment = HorizontalAlignment.Right; + else if (new_alignment == HorizontalAlignment.Right) + new_alignment = HorizontalAlignment.Left; + } + + document.alignment = new_alignment; + + // MS word-wraps if alignment isn't left + if (Multiline) { + if (alignment != HorizontalAlignment.Left) { + document.Wrap = true; + } else { + document.Wrap = word_wrap; + } + } + + for (int i = 1; i <= document.Lines; i++) { + document.GetLine (i).Alignment = new_alignment; + } + + document.RecalculateDocument (CreateGraphicsInternal ()); + + Invalidate (); // Make sure we refresh + } + + internal override Color ChangeBackColor (Color backColor) + { + if (backColor == Color.Empty) { + if (!ReadOnly) + backColor = SystemColors.Window; + + backcolor_set = false; + } + + return backColor; + } + + void OnAutoCompleteCustomSourceChanged(object sender, CollectionChangeEventArgs e) { + if(auto_complete_source == AutoCompleteSource.CustomSource) { + //FIXME: handle add, remove and refresh events in AutoComplete algorithm. + } + } + #endregion // Private & Internal Methods + + #region Public Instance Properties + [MonoTODO("AutoCompletion algorithm is currently not implemented.")] + //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)] + [Browsable (true)] + //[EditorBrowsable (EditorBrowsableState.Always)] + [Localizable (true)] + //[Editor ("ShiftUI.Design.ListWidgetStringCollectionEditor, " + Consts.AssemblySystem_Design, + //"System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)] + public AutoCompleteStringCollection AutoCompleteCustomSource { + get { + if(auto_complete_custom_source == null) { + auto_complete_custom_source = new AutoCompleteStringCollection (); + auto_complete_custom_source.CollectionChanged += new CollectionChangeEventHandler (OnAutoCompleteCustomSourceChanged); + } + return auto_complete_custom_source; + } + set { + if(auto_complete_custom_source == value) + return; + + if(auto_complete_custom_source != null) //remove eventhandler from old collection + auto_complete_custom_source.CollectionChanged -= new CollectionChangeEventHandler (OnAutoCompleteCustomSourceChanged); + + auto_complete_custom_source = value; + + if(auto_complete_custom_source != null) + auto_complete_custom_source.CollectionChanged += new CollectionChangeEventHandler (OnAutoCompleteCustomSourceChanged); + } + } + + [MonoTODO("AutoCompletion algorithm is currently not implemented.")] + [Browsable (true)] + //[EditorBrowsable (EditorBrowsableState.Always)] + [DefaultValue (AutoCompleteMode.None)] + public AutoCompleteMode AutoCompleteMode { + get { return auto_complete_mode; } + set { + if(auto_complete_mode == value) + return; + + if((value < AutoCompleteMode.None) || (value > AutoCompleteMode.SuggestAppend)) + throw new InvalidEnumArgumentException (String.Format ("Enum argument value '{0}' is not valid for AutoCompleteMode", value)); + + auto_complete_mode = value; + } + } + + [MonoTODO("AutoCompletion algorithm is currently not implemented.")] + [Browsable (true)] + //[EditorBrowsable (EditorBrowsableState.Always)] + [DefaultValue (AutoCompleteSource.None)] + [TypeConverter (typeof (TextBoxAutoCompleteSourceConverter))] + public AutoCompleteSource AutoCompleteSource { + get { return auto_complete_source; } + set { + if(auto_complete_source == value) + return; + + if(!Enum.IsDefined (typeof (AutoCompleteSource), value)) + throw new InvalidEnumArgumentException (String.Format ("Enum argument value '{0}' is not valid for AutoCompleteSource", value)); + + auto_complete_source = value; + } + } + + [DefaultValue(false)] + [RefreshProperties (RefreshProperties.Repaint)] + public bool UseSystemPasswordChar { + get { + return use_system_password_char; + } + + set { + if (use_system_password_char != value) { + use_system_password_char = value; + + if (!Multiline) + document.PasswordChar = PasswordChar.ToString (); + else + document.PasswordChar = string.Empty; + Invalidate (); + } + } + } + + [DefaultValue(false)] + [MWFCategory("Behavior")] + public bool AcceptsReturn { + get { + return accepts_return; + } + + set { + if (value != accepts_return) { + accepts_return = value; + } + } + } + + [DefaultValue(CharacterCasing.Normal)] + [MWFCategory("Behavior")] + public CharacterCasing CharacterCasing { + get { + return character_casing; + } + + set { + if (value != character_casing) { + character_casing = value; + } + } + } + + [Localizable(true)] + [DefaultValue('\0')] + [MWFCategory("Behavior")] + [RefreshProperties (RefreshProperties.Repaint)] + public char PasswordChar { + get { + if (use_system_password_char) { + return '*'; + } + return password_char; + } + + set { + if (value != password_char) { + password_char = value; + if (!Multiline) { + document.PasswordChar = PasswordChar.ToString (); + } else { + document.PasswordChar = string.Empty; + } + this.CalculateDocument(); + } + } + } + + [DefaultValue(ScrollBars.None)] + [Localizable(true)] + [MWFCategory("Appearance")] + public ScrollBars ScrollBars { + get { + return (ScrollBars)scrollbars; + } + + set { + if (!Enum.IsDefined (typeof (ScrollBars), value)) + throw new InvalidEnumArgumentException ("value", (int) value, + typeof (ScrollBars)); + + if (value != (ScrollBars)scrollbars) { + scrollbars = (RichTextBoxScrollBars)value; + base.CalculateScrollBars(); + } + } + } + + public override string Text { + get { + return base.Text; + } + + set { + base.Text = value; + } + } + + [DefaultValue(HorizontalAlignment.Left)] + [Localizable(true)] + [MWFCategory("Appearance")] + public HorizontalAlignment TextAlign { + get { + return alignment; + } + + set { + if (value != alignment) { + alignment = value; + + UpdateAlignment (); + + OnTextAlignChanged(EventArgs.Empty); + } + } + } + #endregion // Public Instance Properties + + public void Paste (string text) + { + document.ReplaceSelection (CaseAdjust (text), false); + + ScrollToCaret(); + OnTextChanged(EventArgs.Empty); + } + #region Protected Instance Methods + protected override CreateParams CreateParams { + get { + return base.CreateParams; + } + } + + protected override void Dispose (bool disposing) + { + base.Dispose (disposing); + } + + protected override bool IsInputKey (Keys keyData) + { + return base.IsInputKey (keyData); + } + + protected override void OnGotFocus (EventArgs e) + { + base.OnGotFocus (e); + if (selection_length == -1 && !has_been_focused) + SelectAllNoScroll (); + has_been_focused = true; + } + + protected override void OnHandleCreated (EventArgs e) + { + base.OnHandleCreated (e); + } + + protected virtual void OnTextAlignChanged (EventArgs e) + { + EventHandler eh = (EventHandler)(Events [TextAlignChangedEvent]); + if (eh != null) + eh (this, e); + } + + protected override void WndProc (ref Message m) + { + switch ((Msg)m.Msg) { + case Msg.WM_KEYDOWN: + if (!IsAutoCompleteAvailable) + break; + + Keys key_data = (Keys)m.WParam.ToInt32 (); + switch (key_data) { + case Keys.Down: + case Keys.Up: + case Keys.PageDown: + case Keys.PageUp: + if (NavigateAutoCompleteList (key_data)) { + m.Result = IntPtr.Zero; + return; + } + break; + case Keys.Enter: + if (auto_complete_listbox != null && auto_complete_listbox.Visible) + auto_complete_listbox.HideListBox (false); + SelectAll (); + break; + case Keys.Escape: + if (auto_complete_listbox != null && auto_complete_listbox.Visible) + auto_complete_listbox.HideListBox (false); + break; + case Keys.Delete: + ProcessAutoCompleteInput (ref m, true); + return; + default: + break; + } + break; + case Msg.WM_CHAR: + if (!IsAutoCompleteAvailable) + break; + + // Don't handle either Enter or Esc - they are handled in the WM_KEYDOWN case + int char_value = m.WParam.ToInt32 (); + if (char_value == 13 || char_value == 27) + break; + + ProcessAutoCompleteInput (ref m, char_value == 8); + return; + case Msg.WM_LBUTTONDOWN: + // When the textbox gets focus by LBUTTON (but not by middle or right) + // it does not do the select all / scroll thing. + has_been_focused = true; + FocusInternal (true); + break; + } + + base.WndProc(ref m); + } + #endregion // Protected Instance Methods + + #region Events + static object TextAlignChangedEvent = new object (); + + public event EventHandler TextAlignChanged { + add { Events.AddHandler (TextAlignChangedEvent, value); } + remove { Events.RemoveHandler (TextAlignChangedEvent, value); } + } + #endregion // Events + + #region Private Methods + + + + internal void RestoreContextMenu () + { + //ContextMenuInternal = menu; + } + + private void menu_Popup(object sender, EventArgs e) { + if (SelectionLength == 0) { + cut.Enabled = false; + copy.Enabled = false; + } else { + cut.Enabled = true; + copy.Enabled = true; + } + + if (SelectionLength == TextLength) { + select_all.Enabled = false; + } else { + select_all.Enabled = true; + } + + if (!CanUndo) { + undo.Enabled = false; + } else { + undo.Enabled = true; + } + + if (ReadOnly) { + undo.Enabled = cut.Enabled = paste.Enabled = delete.Enabled = false; + } + } + + private void undo_Click(object sender, EventArgs e) { + Undo(); + } + + private void cut_Click(object sender, EventArgs e) { + Cut(); + } + + private void copy_Click(object sender, EventArgs e) { + Copy(); + } + + private void paste_Click(object sender, EventArgs e) { + Paste(); + } + + private void delete_Click(object sender, EventArgs e) { + SelectedText = string.Empty; + } + + private void select_all_Click(object sender, EventArgs e) { + SelectAll(); + } + #endregion // Private Methods + + public override bool Multiline { + get { + return base.Multiline; + } + + set { + base.Multiline = value; + } + } + + protected override void OnBackColorChanged (EventArgs e) + { + base.OnBackColorChanged (e); + } + + protected override void OnFontChanged (EventArgs e) + { + base.OnFontChanged (e); + } + + protected override void OnHandleDestroyed (EventArgs e) + { + base.OnHandleDestroyed (e); + } + + class AutoCompleteListBox : Widget + { + TextBox owner; + VScrollBar vscroll; + int top_item; + int last_item; + internal int page_size; + int item_height; + int highlighted_index = -1; + bool user_defined_size; + bool resizing; + Rectangle resizer_bounds; + + const int DefaultDropDownItems = 7; + + public AutoCompleteListBox (TextBox tb) + { + owner = tb; + item_height = FontHeight + 2; + + vscroll = new VScrollBar (); + vscroll.ValueChanged += VScrollValueChanged; + Widgets.Add (vscroll); + + is_visible = false; + InternalBorderStyle = BorderStyle.FixedSingle; + } + + protected override CreateParams CreateParams { + get { + CreateParams cp = base.CreateParams; + + cp.Style ^= (int)WindowStyles.WS_CHILD; + cp.Style ^= (int)WindowStyles.WS_VISIBLE; + cp.Style |= (int)WindowStyles.WS_POPUP; + cp.ExStyle |= (int)WindowExStyles.WS_EX_TOPMOST | (int)WindowExStyles.WS_EX_TOOLWINDOW; + return cp; + } + } + + public int HighlightedIndex { + get { + return highlighted_index; + } + set { + if (value == highlighted_index) + return; + + if (highlighted_index != -1) + Invalidate (GetItemBounds (highlighted_index)); + highlighted_index = value; + if (highlighted_index != -1) + Invalidate (GetItemBounds (highlighted_index)); + + if (highlighted_index != -1) + EnsureVisible (highlighted_index); + } + } + + public void Scroll (int lines) + { + int max = vscroll.Maximum - page_size + 1; + int val = vscroll.Value + lines; + if (val > max) + val = max; + else if (val < vscroll.Minimum) + val = vscroll.Minimum; + + vscroll.Value = val; + } + + public void EnsureVisible (int index) + { + if (index < top_item) { + vscroll.Value = index; + } else { + int max = vscroll.Maximum - page_size + 1; + int rows = Height / item_height; + if (index > top_item + rows - 1) { + index = index - rows + 1; + vscroll.Value = index > max ? max : index; + } + } + } + + internal override bool ActivateOnShow { + get { + return false; + } + } + + void VScrollValueChanged (object o, EventArgs args) + { + if (top_item == vscroll.Value) + return; + + top_item = vscroll.Value; + last_item = GetLastVisibleItem (); + Invalidate (); + } + + int GetLastVisibleItem () + { + int top_y = Height; + + for (int i = top_item; i < owner.auto_complete_matches.Count; i++) { + int pos = i - top_item; // relative to visible area + if ((pos * item_height) + item_height >= top_y) + return i; + } + + return owner.auto_complete_matches.Count - 1; + } + + Rectangle GetItemBounds (int index) + { + int pos = index - top_item; + Rectangle bounds = new Rectangle (0, pos * item_height, Width, item_height); + if (vscroll.Visible) + bounds.Width -= vscroll.Width; + + return bounds; + } + + int GetItemAt (Point loc) + { + if (loc.Y > (last_item - top_item) * item_height + item_height) + return -1; + + int retval = loc.Y / item_height; + retval += top_item; + + return retval; + } + + void LayoutListBox () + { + int total_height = owner.auto_complete_matches.Count * item_height; + page_size = Math.Max (Height / item_height, 1); + last_item = GetLastVisibleItem (); + + if (Height < total_height) { + vscroll.Visible = true; + vscroll.Maximum = owner.auto_complete_matches.Count - 1; + vscroll.LargeChange = page_size; + vscroll.Location = new Point (Width - vscroll.Width, 0); + vscroll.Height = Height - item_height; + } else + vscroll.Visible = false; + + resizer_bounds = new Rectangle (Width - item_height, Height - item_height, + item_height, item_height); + } + + public void HideListBox (bool set_text) + { + if (set_text) + owner.Text = owner.auto_complete_matches [HighlightedIndex]; + + Capture = false; + Hide (); + } + + public void ShowListBox () + { + if (!user_defined_size) { + // This should call the Layout routine for us + int height = owner.auto_complete_matches.Count > DefaultDropDownItems ? + DefaultDropDownItems * item_height : (owner.auto_complete_matches.Count + 1) * item_height; + Size = new Size (owner.Width, height); + } else + LayoutListBox (); + + vscroll.Value = 0; + HighlightedIndex = -1; + + Show (); + // make sure we are on top - call the raw routine, since we are parentless + XplatUI.SetZOrder (Handle, IntPtr.Zero, true, false); + Invalidate (); + } + + protected override void OnResize (EventArgs args) + { + base.OnResize (args); + + LayoutListBox (); + Refresh (); + } + + protected override void OnMouseDown (MouseEventArgs args) + { + base.OnMouseDown (args); + + if (!resizer_bounds.Contains (args.Location)) + return; + + user_defined_size = true; + resizing = true; + Capture = true; + } + + protected override void OnMouseMove (MouseEventArgs args) + { + base.OnMouseMove (args); + + if (resizing) { + Point mouse_loc = Widget.MousePosition; + Point ctrl_loc = PointToScreen (Point.Empty); + + Size new_size = new Size (mouse_loc.X - ctrl_loc.X, mouse_loc.Y - ctrl_loc.Y); + if (new_size.Height < item_height) + new_size.Height = item_height; + if (new_size.Width < item_height) + new_size.Width = item_height; + + Size = new_size; + return; + } + + Cursor = resizer_bounds.Contains (args.Location) ? Cursors.SizeNWSE : Cursors.Default; + + int item_idx = GetItemAt (args.Location); + if (item_idx != -1) + HighlightedIndex = item_idx; + } + + protected override void OnMouseUp (MouseEventArgs args) + { + base.OnMouseUp (args); + + int item_idx = GetItemAt (args.Location); + if (item_idx != -1 && !resizing) + HideListBox (true); + + owner.OnAutoCompleteValueSelected (EventArgs.Empty); // internal + resizing = false; + Capture = false; + } + + internal override void OnPaintInternal (PaintEventArgs args) + { + Graphics g = args.Graphics; + Brush brush = ThemeEngine.Current.ResPool.GetSolidBrush (ForeColor); + + int highlighted_idx = HighlightedIndex; + + int y = 0; + int last = GetLastVisibleItem (); + for (int i = top_item; i <= last; i++) { + Rectangle item_bounds = GetItemBounds (i); + if (!item_bounds.IntersectsWith (args.ClipRectangle)) + continue; + + if (i == highlighted_idx) { + g.FillRectangle (SystemBrushes.Highlight, item_bounds); + g.DrawString (owner.auto_complete_matches [i], Font, SystemBrushes.HighlightText, item_bounds); + } else + g.DrawString (owner.auto_complete_matches [i], Font, brush, item_bounds); + + y += item_height; + } + + ThemeEngine.Current.CPDrawSizeGrip (g, SystemColors.Control, resizer_bounds); + } + } + } + + internal class TextBoxAutoCompleteSourceConverter : EnumConverter + { + public TextBoxAutoCompleteSourceConverter(Type type) + : base(type) + { } + + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) + { + StandardValuesCollection stdv = base.GetStandardValues(context); + AutoCompleteSource[] arr = new AutoCompleteSource[stdv.Count]; + stdv.CopyTo(arr, 0); + AutoCompleteSource[] arr2 = Array.FindAll(arr, delegate (AutoCompleteSource value) { + // No "ListItems" in a TextBox. + return value != AutoCompleteSource.ListItems; + }); + return new StandardValuesCollection(arr2); + } + } +} -- cgit v1.2.3