Skip to content

Commit 56c893b

Browse files
committed
Add Mac implementation for DropDownToolItem
- Add ShowDropArrow for all platforms, and ensure it updates at runtime - Mac has two implementations, one that uses NSSegmentedControl (10.14 and earlier) and another that uses NSMenuToolbarItem (10.15 and later)
1 parent 80bff28 commit 56c893b

17 files changed

+356
-50
lines changed

lib/monomac

src/Eto.Gtk/Forms/ToolBar/DropDownToolItemHandler.cs

+24-7
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,23 @@ public class DropDownToolItemHandler : ToolItemHandler<Gtk.ToggleToolButton, Dro
1313
// add a drop arrow to the button text.
1414

1515
private Gtk.Menu dropMenu;
16-
16+
bool showDropArrow = true;
17+
1718
public DropDownToolItemHandler()
1819
{
1920
dropMenu = new Gtk.Menu();
2021
}
21-
22+
2223
#region IToolBarButton Members
2324

24-
25+
2526
#endregion
2627

2728
public override void CreateControl(ToolBarHandler handler, int index)
2829
{
2930
Gtk.Toolbar tb = handler.Control;
3031

31-
var buttonText = ShowDropArrow ? Text + " ▾" : Text;
3232
Control = new Gtk.ToggleToolButton();
33-
Control.Label = buttonText;
3433
Control.IconWidget = GtkImage;
3534
Control.IsImportant = true;
3635
Control.Sensitive = Enabled;
@@ -42,12 +41,30 @@ public override void CreateControl(ToolBarHandler handler, int index)
4241
tb.Insert(Control, index);
4342
Control.Clicked += HandleClicked;
4443
dropMenu.Hidden += HandleMenuClosed;
44+
SetText();
45+
}
46+
47+
protected override void SetText()
48+
{
49+
if (Control != null)
50+
Control.Label = ShowDropArrow ? Text + " ▾" : Text;
4551
}
4652

4753
/// <summary>
4854
/// Gets or sets whether the drop arrow is shown on the button.
4955
/// </summary>
50-
public bool ShowDropArrow { get; set; } = true;
56+
public bool ShowDropArrow
57+
{
58+
get => showDropArrow;
59+
set
60+
{
61+
if (showDropArrow != value)
62+
{
63+
showDropArrow = value;
64+
SetText();
65+
}
66+
}
67+
}
5168

5269
#if GTKCORE
5370
private void HandleClicked(object sender, EventArgs e)
@@ -108,7 +125,7 @@ public void HandleClicked(object sender, EventArgs e)
108125
public void AddMenu(int index, MenuItem item)
109126
{
110127
dropMenu.Insert((Gtk.Widget)item.ControlObject, index);
111-
var handler = item.Handler as Menu.IMenuHandler;
128+
var handler = item.Handler as Menu.IMenuHandler;
112129
//SetChildAccelGroup(item);
113130
}
114131

src/Eto.Gtk/Forms/ToolBar/ToolItemHandler.cs

+39-3
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,56 @@ public interface IToolBarItemHandler
1010
}
1111

1212
public abstract class ToolItemHandler<TControl, TWidget> : WidgetHandler<TControl, TWidget>, ToolItem.IHandler, IToolBarItemHandler
13-
where TControl: Gtk.Widget
13+
where TControl: Gtk.ToolItem
1414
where TWidget: ToolItem
1515
{
1616
bool enabled = true;
1717
bool visible = true;
18+
string text;
19+
string toolTip;
1820
Image image;
1921

2022
protected Gtk.Image GtkImage { get; set; }
2123

2224
public abstract void CreateControl(ToolBarHandler handler, int index);
2325

24-
public string Text { get; set; }
26+
public string Text
27+
{
28+
get => text;
29+
set
30+
{
31+
if (text != value)
32+
{
33+
text = value;
34+
SetText();
35+
}
36+
}
37+
}
38+
39+
public virtual string ToolTip
40+
{
41+
get => toolTip;
42+
set
43+
{
44+
if (toolTip != value)
45+
{
46+
toolTip = value;
47+
SetToolTip();
48+
}
49+
}
50+
}
51+
52+
protected virtual void SetText()
53+
{
54+
if (Control is Gtk.ToolButton button)
55+
button.Label = Text;
56+
}
2557

26-
public string ToolTip { get; set; }
58+
protected virtual void SetToolTip()
59+
{
60+
if (Control is Gtk.ToolButton button)
61+
button.TooltipText = ToolTip;
62+
}
2763

2864
public Image Image
2965
{

src/Eto.Mac/Eto.Mac64.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ You do not need to use any of the classes of this assembly (unless customizing t
4545
<Using Include="System.Double" Alias="nfloat" />
4646
<Using Include="System.Int64" Alias="nint" />
4747
<Using Include="System.UInt64" Alias="nuint" />
48+
<Using Include="System.IntPtr" Alias="NativeHandle" />
4849
</ItemGroup>
4950

5051
<ItemGroup>

src/Eto.Mac/Eto.XamMac2.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<Using Include="CoreFoundation" />
2424
<Using Include="ImageIO" />
2525
<Using Include="CoreText" />
26+
<Using Include="System.IntPtr" Alias="NativeHandle" />
2627
</ItemGroup>
2728

2829
<PropertyGroup>

src/Eto.Mac/Forms/Controls/SegmentedButtonHandler.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -391,9 +391,9 @@ public void InsertItem(int index, SegmentedItem item)
391391
static readonly IntPtr selSetShowsMenuIndicator = Selector.GetHandle("setShowsMenuIndicator:forSegment:");
392392

393393
// 10.13+
394-
static readonly bool supportsTooltip = ObjCExtensions.InstancesRespondToSelector<NSSegmentedCell>(selSetToolTipForSegment);
395-
static readonly bool supportsMenuIndicator = ObjCExtensions.InstancesRespondToSelector<NSSegmentedControl>(selSetShowsMenuIndicator);
396-
394+
internal static readonly bool supportsTooltip = ObjCExtensions.InstancesRespondToSelector<NSSegmentedCell>(selSetToolTipForSegment);
395+
internal static readonly bool supportsMenuIndicator = ObjCExtensions.InstancesRespondToSelector<NSSegmentedControl>(selSetShowsMenuIndicator);
396+
397397
public void SetItem(int index, SegmentedItem item)
398398
{
399399
Control.SetLabel(item.Text ?? string.Empty, index);

src/Eto.Mac/Forms/Menu/MenuHandler.cs

+9
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,15 @@ public interface IMenuHandler
1515
public class EtoMenu : NSMenu
1616
{
1717
public bool WorksWhenModal { get; set; }
18+
19+
public EtoMenu()
20+
{
21+
}
22+
23+
public EtoMenu(NativeHandle handle)
24+
: base(handle)
25+
{
26+
}
1827
}
1928

2029
static class MenuHandler
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
using System;
2+
using Eto.Drawing;
3+
using Eto.Forms;
4+
5+
namespace Eto.Mac.Forms.ToolBar
6+
{
7+
public class DropDownToolItemHandler : ToolItemHandler<NSMenuToolbarItem, DropDownToolItem>, DropDownToolItem.IHandler
8+
{
9+
NSMenu menu;
10+
11+
protected override bool UseButtonStyle => false;
12+
protected override bool UseAction => false;
13+
14+
#if MACOS || XAMMAC2
15+
static readonly IntPtr selInitWithItemIdentifier_Handle = Selector.GetHandle("initWithItemIdentifier:");
16+
17+
// constructor with identifier isn't available in the version of macOS workload we need yet..
18+
class EtoMenuToolbarItem : NSMenuToolbarItem
19+
{
20+
public EtoMenuToolbarItem()
21+
{
22+
}
23+
24+
[Export ("initWithItemIdentifier:")]
25+
public EtoMenuToolbarItem (string itemIdentifier)
26+
: base (NSObjectFlag.Empty)
27+
{
28+
NSApplication.EnsureUIThread ();
29+
if (itemIdentifier == null)
30+
throw new ArgumentNullException ("itemIdentifier");
31+
#if USE_CFSTRING
32+
var nsitemIdentifier = CFString.CreateNative(itemIdentifier);
33+
#else
34+
var nsitemIdentifier = NSString.CreateNative(itemIdentifier);
35+
#endif
36+
37+
if (IsDirectBinding) {
38+
Handle = Messaging.IntPtr_objc_msgSend_IntPtr (this.Handle, selInitWithItemIdentifier_Handle, nsitemIdentifier);
39+
} else {
40+
Handle = Messaging.IntPtr_objc_msgSendSuper_IntPtr (this.SuperHandle, selInitWithItemIdentifier_Handle, nsitemIdentifier);
41+
}
42+
NSString.ReleaseNative (nsitemIdentifier);
43+
44+
}
45+
}
46+
47+
protected override NSMenuToolbarItem CreateControl() => new EtoMenuToolbarItem(Identifier);
48+
#else
49+
protected override NSMenuToolbarItem CreateControl() => new NSMenuToolbarItem(Identifier);
50+
#endif
51+
52+
public bool ShowDropArrow
53+
{
54+
get => Control.ShowsIndicator;
55+
set => Control.ShowsIndicator = value;
56+
}
57+
58+
protected override void Initialize()
59+
{
60+
base.Initialize();
61+
Control.ShowsIndicator = true;
62+
menu = new NSMenu();
63+
menu.AutoEnablesItems = true;
64+
menu.ShowsStateColumn = true;
65+
Control.Menu = menu;
66+
// first item is never shown, it's the "title" of the pull down?? weird.
67+
menu.InsertItem(new NSMenuItem(string.Empty), 0);
68+
}
69+
70+
public void AddMenu(int index, MenuItem item)
71+
{
72+
menu.InsertItem((NSMenuItem)item.ControlObject, index + 1);
73+
}
74+
75+
public void RemoveMenu(MenuItem item)
76+
{
77+
menu.RemoveItem((NSMenuItem)item.ControlObject);
78+
}
79+
80+
public void Clear()
81+
{
82+
menu.RemoveAllItems();
83+
}
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System;
2+
using Eto.Drawing;
3+
using Eto.Forms;
4+
using Eto.Mac.Forms.Controls;
5+
using Eto.Mac.Forms.Menu;
6+
7+
namespace Eto.Mac.Forms.ToolBar
8+
{
9+
/// <summary>
10+
/// Drop down handler pre-catalina. This could probably be done a little better.
11+
/// </summary>
12+
public class DropDownToolItemPreCatalinaHandler : ToolItemHandler<NSToolbarItem, DropDownToolItem>, DropDownToolItem.IHandler
13+
{
14+
ContextMenu contextMenu;
15+
MenuSegmentedItem menuItem;
16+
SegmentedButton segmentedButton;
17+
bool showDropArrow = true;
18+
protected override bool UseButtonStyle => false;
19+
20+
protected override bool IsButton => true;
21+
22+
protected override bool UseAction => false;
23+
24+
public bool ShowDropArrow
25+
{
26+
get => showDropArrow;
27+
set
28+
{
29+
showDropArrow = value;
30+
if (SegmentedButtonHandler.supportsMenuIndicator)
31+
{
32+
var nssegmentedControl = SegmentedButtonHandler.GetControl(segmentedButton);
33+
nssegmentedControl.SetShowsMenuIndicator(value, 0);
34+
}
35+
}
36+
}
37+
38+
protected override void Initialize()
39+
{
40+
contextMenu = new ContextMenu();
41+
42+
menuItem = new MenuSegmentedItem { Menu = contextMenu };
43+
44+
segmentedButton = new SegmentedButton();
45+
segmentedButton.Items.Add(menuItem);
46+
47+
Control.View = segmentedButton.ToNative(true);
48+
49+
base.Initialize();
50+
}
51+
52+
protected override NSMenuItem CreateMenuFormRepresentation()
53+
{
54+
var menu = base.CreateMenuFormRepresentation();
55+
menu.Submenu = contextMenu.ToNS();
56+
return menu;
57+
}
58+
59+
public void AddMenu(int index, MenuItem item)
60+
{
61+
contextMenu.Items.Insert(index, item);
62+
}
63+
64+
public void RemoveMenu(MenuItem item)
65+
{
66+
contextMenu.Items.Remove(item);
67+
}
68+
69+
public void Clear()
70+
{
71+
contextMenu.Items.Clear();
72+
}
73+
74+
protected override void SetImage()
75+
{
76+
base.SetImage();
77+
if (Image is Bitmap bmp)
78+
menuItem.Image = bmp.WithSize(ImageSize, ImageSize);
79+
else if (Image is Icon icon)
80+
menuItem.Image = icon.WithSize(ImageSize, ImageSize);
81+
}
82+
83+
}
84+
}

src/Eto.Mac/Forms/ToolBar/SeparatorToolItemHandler.cs

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public class SeparatorToolItemHandler : ToolItemHandler<NSToolbarItem, Separator
1313

1414
protected override bool IsButton => false;
1515

16+
protected override bool UseButtonStyle => false;
17+
1618
public override string Identifier
1719
{
1820
get

src/Eto.Mac/Forms/ToolBar/ToolBarHandler.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ internal int GetIndex(ToolItem item, bool forInsert = false, bool checkVisible =
271271
else if (curitem.Visible)
272272
{
273273
var nativeItem = nativeItems[idx];
274-
if (nativeItem.Identifier == GetIdentifier(curitem))
274+
if (nativeItem.Identifier == GetIdentifier(curitem) || object.Equals(nativeItem, curitem.ControlObject))
275275
idx++;
276276
}
277277
}

0 commit comments

Comments
 (0)