diff --git a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/FSharpFormattingPanelWidget.fs b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/FSharpFormattingPanelWidget.fs index a5487f4f213..48cb88f58e2 100644 --- a/main/external/fsharpbinding/MonoDevelop.FSharpBinding/FSharpFormattingPanelWidget.fs +++ b/main/external/fsharpbinding/MonoDevelop.FSharpBinding/FSharpFormattingPanelWidget.fs @@ -2,7 +2,7 @@ open Gtk open MonoDevelop.Core -open MonoDevelop.Components.PropertyGrid +open MonoDevelop.DesignerSupport // Handwritten GUI, feel free to edit @@ -21,7 +21,7 @@ type FSharpFormattingPolicyPanelWidget() = let mutable hbox2 : Gtk.HBox = null let mutable vbox4 : Gtk.VBox = null let mutable tableScopes : Gtk.Table = null - let mutable propertyGrid : PropertyGrid = null + let mutable propertyGrid : PropertyGridWrapper = null let getName format = if format = policy.DefaultFormat then @@ -125,12 +125,12 @@ type FSharpFormattingPolicyPanelWidget() = w8.Expand <- false w8.Fill <- false // Container child vbox4.Gtk.Box+BoxChild - propertyGrid <- new PropertyGrid() + propertyGrid <- new PropertyGridWrapper () propertyGrid.Name <- "propertyGrid" propertyGrid.ShowToolbar <- false propertyGrid.ShowHelp <- false - vbox4.Add (propertyGrid) - let w9 = vbox4.[propertyGrid] :?> Gtk.Box.BoxChild + vbox4.Add (propertyGrid.Widget) + let w9 = vbox4.[propertyGrid.Widget] :?> Gtk.Box.BoxChild w9.Position <- 2 hbox1.Add(vbox4) let w10 = hbox1.[vbox4] :?> Gtk.Box.BoxChild diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs index 2c942b6275a..3f38ecf1c68 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs @@ -76,18 +76,13 @@ NSView GetNextFocusableViewForView (NSView view, int nextPositionInArray = 1) public void AddViews (params NSView [] views) => viewsKeyLoopOrder.AddRange (views); - public void FocusNextView (NSView view, int nextPositionInArray = 1) + public void RecalculeNextResponderChain () { - var window = view?.Window; - if (window == null) { - return; - } - var nextView = GetNextFocusableViewForView (view, nextPositionInArray); - if (nextView != null) { - window.MakeFirstResponder (nextView); - } else { - //in case of no view found we follow the next logical view - window.MakeFirstResponder (nextPositionInArray >= 0 ? view.NextKeyView : view.PreviousKeyView); + NSView currentView = viewsKeyLoopOrder.FirstOrDefault (); + while (currentView != null) { + var nextView = GetNextFocusableViewForView (currentView, 1); + currentView.NextKeyView = nextView; + currentView = nextView; } } } @@ -125,15 +120,15 @@ class MacToolbox : NSView, IPropertyPadProvider, IToolboxConfiguration NSTextField messageTextField; - KeyViewLoopDelegate keyViewLoopDelegate; - + readonly KeyViewLoopDelegate keyViewLoopDelegate; + public MacToolbox (ToolboxService toolboxService, IPadWindow container) { WantsLayer = true; keyViewLoopDelegate = new KeyViewLoopDelegate (); - - verticalStackView = new NSStackView () { + + verticalStackView = new UnfocusableStackView () { Orientation = NSUserInterfaceLayoutOrientation.Vertical, Alignment = NSLayoutAttribute.Leading, Spacing = 0, @@ -165,7 +160,6 @@ public MacToolbox (ToolboxService toolboxService, IPadWindow container) filterEntry.AccessibilityTitle = GettextCatalog.GetString ("Search Toolbox"); filterEntry.AccessibilityHelp = GettextCatalog.GetString ("Enter a term to search for it in the toolbox"); filterEntry.Activated += FilterTextChanged; - filterEntry.CommandRaised += FilterEntry_CommandRaised; horizontalStackView.AddArrangedSubview (filterEntry); @@ -178,8 +172,7 @@ public MacToolbox (ToolboxService toolboxService, IPadWindow container) catToggleButton.ToolTip = GettextCatalog.GetString ("Show categories"); catToggleButton.AccessibilityHelp = GettextCatalog.GetString ("Toggle to show categories"); catToggleButton.Activated += ToggleCategorisation; - catToggleButton.KeyDownPressed += OnKeyDownKeyLoop; - + horizontalStackView.AddArrangedSubview (catToggleButton); catToggleButton.SetContentCompressionResistancePriority ((int)NSLayoutPriority.DefaultHigh, NSLayoutConstraintOrientation.Horizontal); @@ -191,8 +184,6 @@ public MacToolbox (ToolboxService toolboxService, IPadWindow container) compactModeToggleButton.AccessibilityTitle = GettextCatalog.GetString ("Compact Layout"); compactModeToggleButton.AccessibilityHelp = GettextCatalog.GetString ("Toggle for toolbox to use compact layout"); compactModeToggleButton.Activated += ToggleCompactMode; - compactModeToggleButton.KeyDownPressed += OnKeyDownKeyLoop; - horizontalStackView.AddArrangedSubview (compactModeToggleButton); @@ -205,7 +196,6 @@ public MacToolbox (ToolboxService toolboxService, IPadWindow container) toolboxAddButton.AccessibilityHelp = GettextCatalog.GetString ("Add toolbox items"); toolboxAddButton.ToolTip = GettextCatalog.GetString ("Add toolbox items"); toolboxAddButton.Activated += ToolboxAddButton_Clicked; - toolboxAddButton.KeyDownPressed += OnKeyDownKeyLoop; horizontalStackView.AddArrangedSubview (toolboxAddButton); @@ -218,7 +208,7 @@ public MacToolbox (ToolboxService toolboxService, IPadWindow container) AccessibilityTitle = GettextCatalog.GetString ("Toolbox Toolbar"), }; - var scrollView = new NSScrollView () { + var scrollView = new UnfocusableScrollview () { HasVerticalScroller = true, HasHorizontalScroller = false, DocumentView = toolboxWidget @@ -261,43 +251,9 @@ public MacToolbox (ToolboxService toolboxService, IPadWindow container) AddSubview (messageTextField); keyViewLoopDelegate.AddViews (filterEntry, catToggleButton, compactModeToggleButton, toolboxAddButton, toolboxWidget); + keyViewLoopDelegate.RecalculeNextResponderChain (); } - #region InternalKeyLoop - - private void FilterEntry_CommandRaised (object sender, NativeViews.SearchTextFieldCommand e) - { - switch (e) { - case NativeViews.SearchTextFieldCommand.InsertBacktab: - keyViewLoopDelegate.FocusNextView ((NSView)sender, -1); - break; - case NativeViews.SearchTextFieldCommand.InsertTab: - keyViewLoopDelegate.FocusNextView ((NSView)sender, 1); - break; - } - } - - private void OnKeyDownKeyLoop (object sender, NativeViews.NSEventArgs args) - { - if (sender is NSView view && keyViewLoopDelegate.Contains (view)) { - if (args.Event.KeyCode == (int)KeyCodes.Tab) { - - if ((int)args.Event.ModifierFlags == (int)KeyModifierFlag.None) { - keyViewLoopDelegate.FocusNextView (view, 1); - args.Handled = true; - return; - } - if ((int)args.Event.ModifierFlags == (int)KeyModifierFlag.Shift) { - keyViewLoopDelegate.FocusNextView (view, -1); - args.Handled = true; - return; - } - } - } - } - - #endregion - NSIndexPath GetFirstVisibleItemIndexPath () { for (int i = 0; i < toolboxWidget.CategoryVisibilities.Count; i++) { @@ -323,7 +279,6 @@ private void ToolboxWidget_KeyDownPressed (object sender, NativeViews.NSEventArg return; } } - OnKeyDownKeyLoop (sender, args); } void SetCustomMessage (string value) @@ -482,7 +437,6 @@ internal void OnUpdateDeleteItem (CommandInfo info) #region GUI population - readonly List items = new List (); Dictionary categories = new Dictionary (); void AddItems (IEnumerable nodes) @@ -534,6 +488,7 @@ public void Refresh () compactModeToggleButton.Hidden = !toolboxWidget.CanIconizeToolboxCategories; compactModeToggleButton.InvalidateIntrinsicContentSize (); + keyViewLoopDelegate.RecalculeNextResponderChain (); if (categories.Count == 0) { SetCustomMessage (GettextCatalog.GetString ("There are no tools available for the current document.")); @@ -553,22 +508,13 @@ protected override void Dispose (bool disposing) if (disposing) { filterEntry.Activated -= FilterTextChanged; - filterEntry.CommandRaised -= FilterEntry_CommandRaised; - catToggleButton.Activated -= ToggleCategorisation; - catToggleButton.KeyDownPressed -= OnKeyDownKeyLoop; - compactModeToggleButton.Activated -= ToggleCompactMode; - compactModeToggleButton.KeyDownPressed -= OnKeyDownKeyLoop; - toolboxAddButton.Activated -= ToolboxAddButton_Clicked; - toolboxAddButton.KeyDownPressed -= OnKeyDownKeyLoop; - toolboxWidget.ActivateSelectedItem -= ToolboxWidget_ActivateSelectedItem; toolboxWidget.MenuOpened -= ToolboxWidget_MenuOpened; toolboxWidget.DragBegin -= ToolboxWidget_DragBegin; toolboxWidget.RegionCollapsed -= FilterTextChanged; - toolboxWidget.KeyDownPressed -= OnKeyDownKeyLoop; toolboxService.ToolboxContentsChanged -= ToolboxService_ToolboxContentsChanged; toolboxService.ToolboxConsumerChanged -= ToolboxService_ToolboxConsumerChanged; @@ -614,6 +560,7 @@ public bool AllowEditingComponents { } set { toolboxAddButton.Hidden = !value; + keyViewLoopDelegate.RecalculeNextResponderChain (); toolboxAddButton.InvalidateIntrinsicContentSize (); } } diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/NativeViews/SearchTextField.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/NativeViews/SearchTextField.cs index 90d0e6f7d02..a6d0ff6aa08 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/NativeViews/SearchTextField.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/NativeViews/SearchTextField.cs @@ -33,20 +33,13 @@ namespace MonoDevelop.DesignerSupport.Toolbox.NativeViews { - enum SearchTextFieldCommand - { - InsertTab, - InsertBacktab - } - - class SearchTextField : NSSearchField, INSSearchFieldDelegate + class SearchTextField : NSSearchField { public event EventHandler Focused; - public event EventHandler CommandRaised; public SearchTextField () { - Delegate = this; + } public override bool BecomeFirstResponder () @@ -54,21 +47,6 @@ public override bool BecomeFirstResponder () Focused?.Invoke (this, EventArgs.Empty); return base.BecomeFirstResponder (); } - - [Export ("control:textView:doCommandBySelector:")] - bool CommandBySelector (NSControl control, NSTextField field, ObjCRuntime.Selector sel) - { - switch (sel.Name) { - case "insertTab:": // down arrow - CommandRaised?.Invoke (this, SearchTextFieldCommand.InsertTab); - return true; - - case "insertBacktab:": // up arrow - CommandRaised?.Invoke (this, SearchTextFieldCommand.InsertBacktab); - return true; - } - return false; - } } } #endif \ No newline at end of file diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/NativeViews/ToggleButton.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/NativeViews/ToggleButton.cs index 3bcb58a0411..aa3cb5a8e38 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/NativeViews/ToggleButton.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/NativeViews/ToggleButton.cs @@ -47,7 +47,6 @@ public NSEventArgs (NSEvent nsEvent) class ToggleButton : NSButton { - public event EventHandler KeyDownPressed; public event EventHandler Focused; public override CGSize IntrinsicContentSize => Hidden ? CGSize.Empty : new CGSize (25, 25); @@ -78,12 +77,6 @@ public override void KeyDown (NSEvent theEvent) if ((int)theEvent.ModifierFlags == (int) KeyModifierFlag.None && (theEvent.KeyCode == (int)KeyCodes.Enter || theEvent.KeyCode == (int)KeyCodes.Space)) { PerformClick (this); } - - var args = new NSEventArgs (theEvent); - KeyDownPressed?.Invoke (this, args); - - if (!args.Handled) - base.KeyDown (theEvent); } } } diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/IPropertyPad.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/IPropertyPad.cs index 18c610f9465..445e893ed94 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/IPropertyPad.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/IPropertyPad.cs @@ -3,7 +3,7 @@ namespace MonoDevelop.DesignerSupport { - public interface IPropertyPad + public interface IPropertyPad : IDisposable { bool IsGridEditing { get; } diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/MacPropertyGrid.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/MacPropertyGrid.cs index 747eeeade9b..245bfdab97f 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/MacPropertyGrid.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/MacPropertyGrid.cs @@ -36,12 +36,15 @@ using Xamarin.PropertyEditing.Mac; using AppKit; using CoreGraphics; +using System.Linq; namespace MonoDevelop.DesignerSupport { - class MacPropertyGrid : NSView, IPropertyGrid + class MacPropertyGrid : NSView { readonly MacPropertyEditorPanel propertyEditorPanel; + readonly MonoDevelopHostResourceProvider hostResourceProvider; + ComponentModelEditorProvider editorProvider; ComponentModelTarget currentSelectedObject; @@ -49,15 +52,39 @@ class MacPropertyGrid : NSView, IPropertyGrid public bool IsEditing => false; + //Small hack to cover the missing Proppy feature to enable/disable the control + public bool Sensitive { get; set; } = true; + public override NSView HitTest (CGPoint aPoint) + { + if (!Sensitive) return null; + return base.HitTest (aPoint); + } + public event EventHandler PropertyGridChanged; public MacPropertyGrid () { - propertyEditorPanel = new MacPropertyEditorPanel (new MonoDevelopHostResourceProvider ()) { + hostResourceProvider = new MonoDevelopHostResourceProvider (); + + propertyEditorPanel = new MacPropertyEditorPanel (hostResourceProvider) { ShowHeader = false }; AddSubview (propertyEditorPanel); + #region Header Proppy Hack + + var subviews = propertyEditorPanel.Subviews; + header = subviews [0]; + propertyList = subviews [1]; + internalTableView = propertyList.Subviews.OfType () + .FirstOrDefault ().DocumentView as NSTableView; + + //we need the second item constrained with the property list + var topConstraint = propertyEditorPanel.Constraints.FirstOrDefault (s => s.FirstItem == propertyList && s.FirstAttribute == NSLayoutAttribute.Top); + border = topConstraint.SecondItem as NSView; + + #endregion + editorProvider = new ComponentModelEditorProvider (); editorProvider.PropertyChanged += EditorProvider_PropertyChanged; @@ -83,6 +110,44 @@ public void BlankPad () editorProvider.Clear (); } + public object CurrentObject { + get => currentSelectedObject.Target; + } + + #region Header Proppy Hack + + NSView header; + NSView propertyList; + NSTableView internalTableView; + NSView border; + + void ShowHeader (bool enabled) + { + var topConstraint = propertyEditorPanel.Constraints.FirstOrDefault (s => s.FirstItem == propertyList && s.FirstAttribute == NSLayoutAttribute.Top); + propertyEditorPanel.RemoveConstraint (topConstraint); + + if (enabled) { + internalTableView.BackgroundColor = hostResourceProvider.GetNamedColor (NamedResources.PadBackgroundColor); + header.Hidden = false; + propertyEditorPanel.AddConstraint (NSLayoutConstraint.Create (this.propertyList, NSLayoutAttribute.Top, NSLayoutRelation.Equal, border, NSLayoutAttribute.Bottom, 1, 0)); + } else { + internalTableView.BackgroundColor = NSColor.Clear; + header.Hidden = true; + propertyEditorPanel.AddConstraint (NSLayoutConstraint.Create (this.propertyList, NSLayoutAttribute.Top, NSLayoutRelation.Equal, propertyEditorPanel, NSLayoutAttribute.Top, 1, 0)); + } + } + + //HACK: this + public bool ToolbarVisible { + get => !header.Hidden; + set { + //we ensure remove current constraints from proppy + ShowHeader (value); + } + } + + #endregion + public void SetCurrentObject (object lastComponent, object [] propertyProviders) { if (lastComponent != null) { @@ -97,21 +162,6 @@ public void SetCurrentObject (object lastComponent, object [] propertyProviders) } } - public void Populate (bool saveEditSession) - { - //not implemented - } - - public void SetToolbarProvider (Components.PropertyGrid.PropertyGrid.IToolbarProvider toolbarProvider) - { - //not implemented - } - - public void OnPadContentShown () - { - //not implemented - } - protected override void Dispose (bool disposing) { if (disposing) { diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditor/ComponentModelEditorProvider.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditor/ComponentModelEditorProvider.cs index 2d9ebdac6ee..5a95a0b4bb0 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditor/ComponentModelEditorProvider.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditor/ComponentModelEditorProvider.cs @@ -116,7 +116,7 @@ protected static DescriptorPropertyInfo CreatePropertyInfo (System.ComponentMode { var typeDescriptorContext = new TypeDescriptorContext (propertyProvider, propertyDescriptor); - var valueSources = ValueSources.Local | ValueSources.Default; + var valueSources = ValueSources.Default; if (propertyDescriptor.PropertyType.IsEnum) { if (propertyDescriptor.PropertyType.IsDefined (typeof (FlagsAttribute), inherit: false)) return new FlagDescriptorPropertyInfo (typeDescriptorContext, valueSources); diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditor/ComponentModelObjectEditor.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditor/ComponentModelObjectEditor.cs index 501d7d13975..edd4971fe59 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditor/ComponentModelObjectEditor.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditor/ComponentModelObjectEditor.cs @@ -35,12 +35,11 @@ namespace MonoDevelop.DesignerSupport { class ComponentModelObjectEditor - : IObjectEditor, INameableObject, IDisposable + : IObjectEditor, IDisposable { internal const string ComboSeparatorString = "--"; private readonly ComponentModelTarget propertyItem; - public string Name { get; private set; } static IReadOnlyList defaultHandlerList = new List ().AsReadOnly (); static AssignableTypesResult defaultAssignableTypeResult = new AssignableTypesResult (new List ().AsReadOnly ()); @@ -84,7 +83,7 @@ public Task GetAssignableTypesAsync (IPropertyInfo proper public Task> GetHandlersAsync (IEventInfo ev) => Task.FromResult(defaultHandlerList); - public Task GetNameAsync () => Task.FromResult (Name); + public Task GetNameAsync () => Task.FromResult (null); public Task> GetPropertyVariantsAsync (IPropertyInfo property) => Task.FromResult> (Array.Empty ()); @@ -98,22 +97,16 @@ public Task> GetValueAsync (IPropertyInfo property, PropertyVari return Task.FromException> (new ArgumentException ($"Property should be a {nameof (DescriptorPropertyInfo)}", nameof (property))); } - T value = propertyInfo.GetValue (this); + T value = propertyInfo.GetValue (this.Target); var valueInfo = new ValueInfo { Value = value, - Source = ValueSource.Local, + Source = ValueSource.Default, }; return Task.FromResult (valueInfo); } public Task RemovePropertyVariantAsync (IPropertyInfo property, PropertyVariation variant) => Task.CompletedTask; - public Task SetNameAsync (string name) - { - Name = name; - return Task.CompletedTask; - } - public Task SetValueAsync (IPropertyInfo propertyInfo, ValueInfo value, PropertyVariation variations = null) { try { @@ -121,7 +114,17 @@ public Task SetValueAsync (IPropertyInfo propertyInfo, ValueInfo value, Pr return Task.FromException (new ArgumentNullException (nameof (propertyInfo))); if (propertyInfo is DescriptorPropertyInfo info && info.CanWrite) { - info.SetValue (this, value.Value); + + var actualValue = info.GetValue (this.Target); + if (info.GetValue (this.Target).Equals (value.Value)) { + return Task.CompletedTask; + } + + //this is an exception for null/empty cases of string + if (typeof (T) == typeof(string) && string.IsNullOrEmpty (actualValue as string) && string.IsNullOrEmpty (value.Value as string)) { + return Task.CompletedTask; + } + info.SetValue (this.Target, value.Value); RaisePropertyChanged (info); } else { return Task.FromException (new ArgumentException ($"Property should be a writeable {nameof (DescriptorPropertyInfo)}.", nameof (propertyInfo))); diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditor/PropertyInfo/DescriptorPropertyInfo.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditor/PropertyInfo/DescriptorPropertyInfo.cs index be7ead2146c..5a524f4243d 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditor/PropertyInfo/DescriptorPropertyInfo.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/NativePropertyEditor/PropertyInfo/DescriptorPropertyInfo.cs @@ -145,18 +145,33 @@ public static ITypeInfo ToTypeInfo (Type type, bool isRelevant = true) internal virtual T GetValue (object target) { - T converted = default; + var converted = default (T); object value = null; bool canConvert = false; + try { + value = PropertyDescriptor.GetValue (PropertyProvider); + var currentType = typeof (T); + var underlyingType = Nullable.GetUnderlyingType (currentType); + + //proppy has some editors based in nullable types + if (underlyingType != null) { + var converterNullable = new NullableConverter (currentType); + if (converterNullable.CanConvertFrom (underlyingType)) { + object notNullableValue = Convert.ChangeType (value, underlyingType); + return (T)converterNullable.ConvertFrom (notNullableValue); + } + } + var tc = PropertyDescriptor.Converter; - canConvert = tc.CanConvertTo (typeof (T)); + canConvert = tc.CanConvertTo (currentType); if (canConvert) { - converted = (T)tc.ConvertTo (value, typeof (T)); + converted = (T)tc.ConvertTo (value, currentType); } else { - converted = (T)value; + converted = (T)Convert.ChangeType (value, currentType); } + } catch (Exception ex) { LogInternalError ($"Error trying to get and convert value:'{value}' canconvert: {canConvert} T:{typeof (T).FullName} ", ex); } @@ -167,21 +182,22 @@ internal virtual void SetValue (object target, T value) { try { - var currentType = typeof (T) ; + var currentType = typeof (T); //TODO: Proppy in Boolean types uses bool? to handle it, but this will fail using converters //thats because we need ensure take the underlying type currentType = Nullable.GetUnderlyingType (currentType) ?? currentType; - object notNulleableValue = Convert.ChangeType (value, currentType); + object notNullableValue; var tc = PropertyDescriptor.Converter; if (tc.CanConvertFrom (currentType)) { - var result = tc.ConvertFrom (notNulleableValue); - PropertyDescriptor.SetValue (PropertyProvider, result); + notNullableValue = tc.ConvertFrom (value); } else { - notNulleableValue = Convert.ChangeType (value, this.Type); - PropertyDescriptor.SetValue (PropertyProvider, notNulleableValue); + notNullableValue = Convert.ChangeType (value, currentType); } + + PropertyDescriptor.SetValue (PropertyProvider, notNullableValue); + } catch (Exception ex) { LogInternalError ($"Error trying to set and convert a value: {value} T:{typeof (T).FullName}", ex); } diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/PropertyPad.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/PropertyPad.cs index b2f18145e37..50b396aa633 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/PropertyPad.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/PropertyPad.cs @@ -41,21 +41,201 @@ using MonoDevelop.Components; using System; using Gtk; -using MonoDevelop.Core.FeatureConfiguration; namespace MonoDevelop.DesignerSupport { - public class PropertyPad : PadContent, ICommandDelegator, IPropertyPad + class PropertyMacHostWidget : IPropertyGrid { public event EventHandler PropertyGridChanged; - readonly bool isNative; - readonly IPropertyGrid propertyGrid; + readonly GtkNSViewHost host; + + MacPropertyGrid view; + + public string Name { get; set; } + public bool ShowHelp { get; set; } //not implemented + + public ShadowType ShadowType { get; set; } //not implemented + public Widget Widget => host; + + public bool IsGridEditing => view.IsEditing; + + public bool ShowToolbar { + get => view.ToolbarVisible; + set => view.ToolbarVisible = value; + } + + public bool Sensitive { + get => view.Sensitive; + set => view.Sensitive = value; + } + + public object CurrentObject { + get => view.CurrentObject; + set { + view.SetCurrentObject (value, new object [] { value }); + } + } + + public PropertyMacHostWidget () + { + view = new MacPropertyGrid (); + host = new GtkNSViewHost (view); + + view.PropertyGridChanged += View_PropertyGridChanged; + } + + void View_PropertyGridChanged (object sender, EventArgs e) + => PropertyGridChanged?.Invoke (this, e); + + public void SetCurrentObject (object obj, object [] propertyProviders) + => view.SetCurrentObject (obj, propertyProviders); + + public void BlankPad () => view.BlankPad (); + public void Hide () => view.Hidden = true; + public void Show () => view.Hidden = false; + + public void OnPadContentShown () + { + //not implemented; + } + + public void PopulateGrid (bool saveEditSession) + { + //view.SetCurrentObject (obj, propertyProviders); + } + + public void SetToolbarProvider (object toolbarProvider) + { + //not implemented; + } + + public void CommitPendingChanges () + { + //not implemented; + } + + public void Dispose () + { + if (view != null) { + view.PropertyGridChanged -= View_PropertyGridChanged; + view.Dispose (); + view = null; + } + } + } + + public interface IPropertyGrid : IPropertyPad + { + bool ShowToolbar { get; set; } + bool ShowHelp { get; set; } + bool Sensitive { get; set; } + string Name { get; set; } + object CurrentObject { get; set; } + + Gtk.Widget Widget { get; } + ShadowType ShadowType { get; set; } + + void Hide (); + void Show (); + + void SetToolbarProvider (object toolbarProvider); + void CommitPendingChanges (); + } + + public class PropertyGridWrapper : IPropertyGrid + { + public string Name { + get => nativeWidget.Name; + set => nativeWidget.Name = value; + } + + public event EventHandler PropertyGridChanged; + + public Gtk.Widget Widget => nativeWidget.Widget; + + public bool ShowToolbar { + get => nativeWidget.ShowToolbar; + set => nativeWidget.ShowToolbar = value; + } + + public ShadowType ShadowType { + get => nativeWidget.ShadowType; + set => nativeWidget.ShadowType = value; + } + + public bool ShowHelp { + get => nativeWidget.ShowHelp; + set => nativeWidget.ShowHelp = value; + } + + public bool Sensitive { + get => nativeWidget.Sensitive; + set => nativeWidget.Sensitive = value; + } + + public bool IsGridEditing => nativeWidget.IsGridEditing; + + public object CurrentObject { + get => nativeWidget.CurrentObject; + set => nativeWidget.CurrentObject = value; + } + + IPropertyGrid nativeWidget; + + public PropertyGridWrapper () + { #if MAC - MacPropertyGrid nativeGrid; - Gtk.Widget gtkWidget; + nativeWidget = new PropertyMacHostWidget (); +#else + nativeWidget = new pg.PropertyGrid (); #endif - pg.PropertyGrid grid; + nativeWidget.PropertyGridChanged += NativeWidget_PropertyGridChanged; + } + + private void NativeWidget_PropertyGridChanged (object sender, EventArgs e) + => PropertyGridChanged?.Invoke (this, e); + + public void BlankPad () + => nativeWidget.BlankPad (); + + public void PopulateGrid (bool saveEditSession) + => nativeWidget.PopulateGrid (saveEditSession); + + public void SetCurrentObject (object lastComponent, object [] propertyProviders) + => nativeWidget.SetCurrentObject (lastComponent, propertyProviders); + + public void Show () => nativeWidget.Show (); + public void Hide () => nativeWidget.Hide (); + + public void Dispose () + { + if (nativeWidget != null) { + nativeWidget.PropertyGridChanged += NativeWidget_PropertyGridChanged; + nativeWidget.Dispose (); + nativeWidget = null; + } + } + + public void SetToolbarProvider (object toolbarProvider) + { + nativeWidget.SetToolbarProvider (toolbarProvider); + } + + public void OnPadContentShown () + { + //not implemented + } + + public void CommitPendingChanges () => + nativeWidget.CommitPendingChanges (); + } + + public class PropertyPad : PadContent, ICommandDelegator, IPropertyPad + { + public event EventHandler PropertyGridChanged; + + readonly bool isNative; InvisibleFrame frame; bool customWidget; @@ -64,45 +244,28 @@ public class PropertyPad : PadContent, ICommandDelegator, IPropertyPad internal object CommandRouteOrigin { get; set; } + readonly PropertyGridWrapper propertyGridWrapper; public PropertyPad () { frame = new InvisibleFrame (); -#if MAC - isNative = true; - - if (isNative) { - - nativeGrid = new MacPropertyGrid (); - propertyGrid = nativeGrid; - nativeGrid.PropertyGridChanged += Grid_Changed; - gtkWidget = new GtkNSViewHost (nativeGrid); + propertyGridWrapper = new PropertyGridWrapper (); + frame.Add (propertyGridWrapper.Widget); + propertyGridWrapper.PropertyGridChanged += Grid_Changed; - frame.Add (gtkWidget); - } else { -#endif - grid = new pg.PropertyGrid (); - propertyGrid = grid; - grid.Changed += Grid_Changed; - frame.Add (grid); -#if MAC - } -#endif frame.ShowAll (); } - void Grid_Changed (object sender, EventArgs e) - { + void Grid_Changed (object sender, EventArgs e) => PropertyGridChanged?.Invoke (this, e); - } protected override void Initialize (IPadWindow container) { base.Initialize (container); toolbarProvider.Attach (container.GetToolbar (DockPositionType.Top)); - propertyGrid.SetToolbarProvider (toolbarProvider); + propertyGridWrapper.SetToolbarProvider (toolbarProvider); #if MAC //native cocoa needs content shown to initialize stuff @@ -132,14 +295,11 @@ public override void Dispose() if (isNative) { container.PadContentShown -= Window_PadContentShown; container.PadContentHidden -= Window_PadContentHidden; - nativeGrid.PropertyGridChanged -= Grid_Changed; - } else { -#endif - grid.Changed -= Grid_Changed; -#if MAC } #endif - propertyGrid.Dispose (); + + propertyGridWrapper.PropertyGridChanged -= Grid_Changed; + propertyGridWrapper.Dispose (); DesignerSupport.Service.SetPad (null); base.Dispose (); } @@ -164,7 +324,7 @@ object ICommandDelegator.GetDelegatedCommandTarget () public bool IsGridEditing { get { AttachToolbarIfCustomWidget (); - return propertyGrid.IsEditing; + return propertyGridWrapper.IsGridEditing; } } @@ -173,7 +333,7 @@ public bool IsGridEditing { internal pg.PropertyGrid PropertyGrid { get { AttachToolbarIfCustomWidget (); - return isNative ? pGrid : grid; + return isNative ? pGrid : (pg.PropertyGrid) propertyGridWrapper.Widget; } } @@ -182,14 +342,14 @@ public void BlankPad () if (isNative) { AttachToolbarIfCustomWidget (); } - propertyGrid.BlankPad (); + propertyGridWrapper.BlankPad (); CommandRouteOrigin = null; } #if MAC void Window_PadContentShown (object sender, EventArgs e) { - propertyGrid.OnPadContentShown (); + propertyGridWrapper.OnPadContentShown (); if (customWidget && frame.Child is GtkNSViewHost viewHost) { viewHost.Visible = true; @@ -212,10 +372,10 @@ void AttachToolbarIfCustomWidget () #if MAC if (isNative) { - frame.Add (gtkWidget); + frame.Add (propertyGridWrapper.Widget); } else { #endif - frame.Add (grid); + frame.Add (propertyGridWrapper.Widget); #if MAC } #endif @@ -248,12 +408,12 @@ void ClearToolbar () public void SetCurrentObject (object lastComponent, object [] propertyProviders) { AttachToolbarIfCustomWidget (); - propertyGrid.SetCurrentObject (lastComponent, propertyProviders); + propertyGridWrapper.SetCurrentObject (lastComponent, propertyProviders); } public void PopulateGrid (bool saveEditSession) { - propertyGrid.Populate (saveEditSession); + propertyGridWrapper.PopulateGrid (saveEditSession); } } diff --git a/main/src/addins/Xml/Formatting/XmlFormattingPolicyPanelWidget.cs b/main/src/addins/Xml/Formatting/XmlFormattingPolicyPanelWidget.cs index 81cc6c8fd8c..18fc9f6030a 100644 --- a/main/src/addins/Xml/Formatting/XmlFormattingPolicyPanelWidget.cs +++ b/main/src/addins/Xml/Formatting/XmlFormattingPolicyPanelWidget.cs @@ -59,7 +59,7 @@ public XmlFormattingPolicyPanelWidget () Button buttonRemove; Label labelScopes; Table tableScopes; - MonoDevelop.Components.PropertyGrid.PropertyGrid propertyGrid; + DesignerSupport.PropertyGridWrapper propertyGrid; Button buttonAdvanced; void Build () @@ -85,7 +85,7 @@ void Build () ColumnSpacing = 6 }; - propertyGrid = new MonoDevelop.Components.PropertyGrid.PropertyGrid { + propertyGrid = new DesignerSupport.PropertyGridWrapper { ShowToolbar = false, ShowHelp = false }; @@ -107,7 +107,7 @@ void Build () var rightVBox = new VBox (false, 6); rightVBox.PackStart (labelScopes, false, false, 0); rightVBox.PackStart (tableScopes, false, false, 0); - rightVBox.PackStart (propertyGrid, true, true, 0); + rightVBox.PackStart (propertyGrid.Widget, true, true, 0); var mainBox = new HBox (false, 6); mainBox.PackStart (boxScopes, false, false, 0); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs index 4dbc6ad46bb..59acd526a8b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.Commands/CommandManager.cs @@ -437,6 +437,49 @@ bool IsCommandBinding (object commandId, string binding) } return false; } + + void SimulateKeyDownInView (AppKit.NSView view, AppKit.NSEvent currentEvent, AppKit.NSWindow window) + { + if (currentEvent.KeyCode == (ushort)AppKit.NSKey.Tab) { + var expectedKeyView = FindValidKeyView (view); + AppKit.NSView next = null; + if (currentEvent.ModifierFlags.HasFlag (AppKit.NSEventModifierMask.ShiftKeyMask)) { + next = expectedKeyView.PreviousValidKeyView; + } else { + next = expectedKeyView.NextValidKeyView; + } + + view.KeyDown (currentEvent); + if (next != null && window?.FirstResponder != next) { + window.MakeFirstResponder (next); + } + } else { + view.KeyDown (currentEvent); + } + } + + private void SimulateViewKeyActionBehaviour (AppKit.NSView view, AppKit.NSEvent currentEvent) + { + if (view is AppKit.NSButton btn && (currentEvent.KeyCode == (ushort)AppKit.NSKey.Space || currentEvent.KeyCode == (ushort)AppKit.NSKey.Return)) { + btn.PerformClick (btn); + } + } + + static AppKit.NSView FindValidKeyView (AppKit.NSView view) + { + if (view == null) + return null; + + if (view.AcceptsFirstResponder ()) { + if (view.Superview?.Superview is AppKit.NSControl control && control.CurrentEditor == view) { + return FindValidKeyView (control); + } + return view; + } + + return FindValidKeyView (view.Superview); + } + #endif [GLib.ConnectBefore] @@ -453,6 +496,8 @@ void OnKeyReleased (object o, Gtk.KeyReleaseEventArgs e) var window = currentEvent?.Window; var firstResponder = window?.FirstResponder; + bool retVal = false; + // GTK eats FlagsChanged events and this is just to inform // modifier keys changed state, hence always send it to // focused view @@ -467,10 +512,22 @@ void OnKeyReleased (object o, Gtk.KeyReleaseEventArgs e) // KeyboardShortcut[] accels = KeyBindingManager.AccelsFromKey (e.Event, out complete); + if (currentEvent != null && + currentEvent.Type == AppKit.NSEventType.KeyUp && + firstResponder is AppKit.NSView view && + view != window.ContentView) { + + view.KeyUp (currentEvent); + SimulateViewKeyActionBehaviour (view, currentEvent); + retVal = true; + } + if (!complete) { // incomplete accel NotifyIncompleteKeyReleased (e.Event); } + + e.RetVal = retVal; } internal bool ProcessKeyEvent (Gdk.EventKey ev) @@ -507,13 +564,14 @@ internal bool ProcessKeyEvent (Gdk.EventKey ev) if (currentEvent != null && currentEvent.Type == AppKit.NSEventType.KeyDown && - firstResponder != null && - firstResponder != window.ContentView) { - firstResponder.KeyDown (currentEvent); + firstResponder is AppKit.NSView view && + view != window.ContentView) { + + SimulateKeyDownInView (view, currentEvent, window); + return true; } #endif - return false; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/IPropertyGrid.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/IPropertyGrid.cs index 802c392dbc5..7aa546a43cf 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/IPropertyGrid.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/IPropertyGrid.cs @@ -45,5 +45,7 @@ public interface IPropertyGrid : IDisposable void BlankPad (); void OnPadContentShown (); void Populate (bool saveEditSession); + + public Gtk.Widget Widget { get; } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGrid.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGrid.cs index 9d2330cbe2f..1077197a218 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGrid.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.PropertyGrid/PropertyGrid.cs @@ -75,7 +75,9 @@ public class PropertyGrid: Gtk.VBox, IPropertyGrid PropertySort propertySort = PropertySort.Categorized; const string PROP_HELP_KEY = "MonoDevelop.PropertyPad.ShowHelp"; - + + public Widget Widget => this; + public PropertyGrid (): this (new EditorManager ()) { } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/GtkNSViewHost.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/GtkNSViewHost.cs index 61c06c4b806..37c7ded0b0f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/GtkNSViewHost.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/GtkNSViewHost.cs @@ -52,9 +52,6 @@ public sealed class GtkNSViewHost : Widget [DllImport (LIBGTKQUARTZ)] static extern IntPtr gdk_quartz_window_get_nsview (IntPtr window); - [DllImport (LIBGTKQUARTZ)] - static extern IntPtr gdk_quartz_event_get_nsevent (IntPtr evnt); - [DllImport (LIBGTKQUARTZ)] static extern void gdk_window_coords_to_parent ( IntPtr window, @@ -66,15 +63,6 @@ static extern void gdk_window_coords_to_parent ( [DllImport (LIBGTKQUARTZ)] static extern bool gdk_window_has_native (IntPtr window); - static NSEvent GetNSEvent (Gdk.Event evnt) - { - if (evnt == null || evnt.Handle == IntPtr.Zero) - return null; - - var nsEventHandle = gdk_quartz_event_get_nsevent (evnt.Handle); - return Runtime.GetNSObject (nsEventHandle); - } - NSView view; NSView superview; bool disposeViewOnGtkDestroy; @@ -361,50 +349,6 @@ protected override bool OnWidgetEvent (Gdk.Event evnt) } } - bool ForwardEvent ( - TEvent evnt, - Action forwardCall, - Func baseCall) where TEvent : Gdk.Event - { - var acceptsFirstResponderView = GetAcceptsFirstResponderView (); - if (acceptsFirstResponderView == null) - return false; - - var nsEvent = GetNSEvent (evnt); - if (nsEvent == null) - return false; - - forwardCall (acceptsFirstResponderView, nsEvent); - - return baseCall (evnt); - } - - protected override bool OnKeyPressEvent (Gdk.EventKey evnt) - { - LogEnter (); - try { - return ForwardEvent ( - evnt, - (v, e) => v.KeyDown (e), - base.OnKeyReleaseEvent); - } finally { - LogExit (); - } - } - - protected override bool OnKeyReleaseEvent (Gdk.EventKey evnt) - { - LogEnter (); - try { - return ForwardEvent ( - evnt, - (v, e) => v.KeyUp (e), - base.OnKeyReleaseEvent); - } finally { - LogExit (); - } - } - #region Tracing int traceDepth; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/NativeViewHelper.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/NativeViewHelper.cs index f5c475a6831..ca9a648099c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/NativeViewHelper.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/Mac/NativeViewHelper.cs @@ -32,9 +32,22 @@ namespace MonoDevelop.Components.Mac { + internal class UnfocusableScrollview : NSScrollView + { + public override bool AcceptsFirstResponder () => false; + } + + internal class UnfocusableStackView : NSStackView + { + public override bool AcceptsFirstResponder () + { + return false; + } + } + static class NativeViewHelper { - public static NSStackView CreateVerticalStackView (int spacing = 10, bool translatesAutoresizingMaskIntoConstraints = false) => new NSStackView () { + public static NSStackView CreateVerticalStackView (int spacing = 10, bool translatesAutoresizingMaskIntoConstraints = false) => new UnfocusableStackView () { Orientation = NSUserInterfaceLayoutOrientation.Vertical, Alignment = NSLayoutAttribute.Leading, Spacing = spacing, @@ -42,7 +55,7 @@ static class NativeViewHelper TranslatesAutoresizingMaskIntoConstraints = translatesAutoresizingMaskIntoConstraints }; - public static NSStackView CreateHorizontalStackView (int spacing = 10) => new NSStackView () { + public static NSStackView CreateHorizontalStackView (int spacing = 10) => new UnfocusableStackView () { Orientation = NSUserInterfaceLayoutOrientation.Horizontal, Alignment = NSLayoutAttribute.CenterY, Spacing = spacing, diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs index 7a048654c08..5c95d9d7285 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs @@ -394,6 +394,7 @@ public static int RunCustomDialog (Dialog dlg, Window parent) try { Xwt.MessageDialog.RootWindow = Xwt.Toolkit.CurrentEngine.WrapWindow (dialog); IdeApp.DisableIdleActions (); + IdeApp.CommandService.RegisterTopWindow (dialog); int result = GtkWorkarounds.RunDialogWithNotification (dialog); // Focus parent window once the dialog is ran, as focus gets lost if (parent != null) {