Moving PopupMenu to Agg
This commit is contained in:
parent
6f3fe2fddc
commit
2bd1cd1e13
20 changed files with 350 additions and 1186 deletions
|
|
@ -53,7 +53,7 @@ namespace MatterHackers.MatterControl
|
|||
this.HAnchor = HAnchor.Fit;
|
||||
this.Margin = 0;
|
||||
|
||||
this.DynamicPopupContent = () => BrandMenuButton.CreatePopupMenu();
|
||||
this.DynamicPopupContent = () => BrandMenuButton.CreatePopupMenu(theme);
|
||||
|
||||
var row = new FlowLayoutWidget()
|
||||
{
|
||||
|
|
@ -80,7 +80,7 @@ namespace MatterHackers.MatterControl
|
|||
}
|
||||
}
|
||||
|
||||
private static PopupMenu CreatePopupMenu()
|
||||
private static PopupMenu CreatePopupMenu(ThemeConfig theme)
|
||||
{
|
||||
var menuTheme = ApplicationController.Instance.MenuTheme;
|
||||
|
||||
|
|
|
|||
|
|
@ -45,26 +45,26 @@ namespace MatterHackers.MatterControl
|
|||
|
||||
private static double MicroButtonWidth => 30 * GuiWidget.DeviceScale;
|
||||
|
||||
public static SectionWidget ApplyBoxStyle(this ThemeConfig config, SectionWidget sectionWidget)
|
||||
public static SectionWidget ApplyBoxStyle(this ThemeConfig theme, SectionWidget sectionWidget)
|
||||
{
|
||||
return config.ApplyBoxStyle(
|
||||
return theme.ApplyBoxStyle(
|
||||
sectionWidget,
|
||||
config.SectionBackgroundColor,
|
||||
margin: new BorderDouble(config.DefaultContainerPadding, 0, config.DefaultContainerPadding, config.DefaultContainerPadding));
|
||||
theme.SectionBackgroundColor,
|
||||
margin: new BorderDouble(theme.DefaultContainerPadding, 0, theme.DefaultContainerPadding, theme.DefaultContainerPadding));
|
||||
}
|
||||
|
||||
// ApplySquareBoxStyle
|
||||
public static SectionWidget ApplyBoxStyle(this ThemeConfig config, SectionWidget sectionWidget, BorderDouble margin)
|
||||
public static SectionWidget ApplyBoxStyle(this ThemeConfig theme, SectionWidget sectionWidget, BorderDouble margin)
|
||||
{
|
||||
sectionWidget.BackgroundColor = config.SectionBackgroundColor;
|
||||
sectionWidget.BackgroundColor = theme.SectionBackgroundColor;
|
||||
sectionWidget.Margin = 0;
|
||||
sectionWidget.Border = new BorderDouble(bottom: 1);
|
||||
sectionWidget.BorderColor = config.RowBorder;
|
||||
sectionWidget.BorderColor = theme.RowBorder;
|
||||
|
||||
return sectionWidget;
|
||||
}
|
||||
|
||||
public static SectionWidget ApplyBoxStyle(this ThemeConfig config, SectionWidget sectionWidget, Color backgroundColor, BorderDouble margin)
|
||||
public static SectionWidget ApplyBoxStyle(this ThemeConfig theme, SectionWidget sectionWidget, Color backgroundColor, BorderDouble margin)
|
||||
{
|
||||
// Enforce panel padding
|
||||
// sectionWidget.ContentPanel.Padding = new BorderDouble(10, 0, 10, 2);
|
||||
|
|
@ -78,11 +78,11 @@ namespace MatterHackers.MatterControl
|
|||
return sectionWidget;
|
||||
}
|
||||
|
||||
public static void ApplyPrimaryActionStyle(this ThemeConfig config, GuiWidget guiWidget)
|
||||
public static void ApplyPrimaryActionStyle(this ThemeConfig theme, GuiWidget guiWidget)
|
||||
{
|
||||
guiWidget.BackgroundColor = new Color(config.AccentMimimalOverlay, 50);
|
||||
guiWidget.BackgroundColor = new Color(theme.AccentMimimalOverlay, 50);
|
||||
|
||||
Color hoverColor = config.AccentMimimalOverlay;
|
||||
Color hoverColor = theme.AccentMimimalOverlay;
|
||||
|
||||
switch (guiWidget)
|
||||
{
|
||||
|
|
@ -100,28 +100,28 @@ namespace MatterHackers.MatterControl
|
|||
}
|
||||
}
|
||||
|
||||
public static SolidSlider ApplySliderStyle(this ThemeConfig config, SolidSlider solidSlider)
|
||||
public static SolidSlider ApplySliderStyle(this ThemeConfig theme, SolidSlider solidSlider)
|
||||
{
|
||||
solidSlider.View.TrackColor = config.SlightShade;
|
||||
solidSlider.View.TrackColor = theme.SlightShade;
|
||||
solidSlider.View.TrackRadius = 4;
|
||||
|
||||
return solidSlider;
|
||||
}
|
||||
|
||||
public static DoubleSolidSlider ApplySliderStyle(this ThemeConfig config, DoubleSolidSlider solidSlider)
|
||||
public static DoubleSolidSlider ApplySliderStyle(this ThemeConfig theme, DoubleSolidSlider solidSlider)
|
||||
{
|
||||
solidSlider.View.TrackColor = config.SlightShade;
|
||||
solidSlider.View.TrackColor = theme.SlightShade;
|
||||
solidSlider.View.TrackRadius = 4;
|
||||
|
||||
return solidSlider;
|
||||
}
|
||||
|
||||
public static JogControls.ExtrudeButton CreateExtrudeButton(this ThemeConfig config, PrinterConfig printer, string label, double movementFeedRate, int extruderNumber, bool levelingButtons = false)
|
||||
public static JogControls.ExtrudeButton CreateExtrudeButton(this ThemeConfig theme, PrinterConfig printer, string label, double movementFeedRate, int extruderNumber, bool levelingButtons = false)
|
||||
{
|
||||
return new JogControls.ExtrudeButton(printer, label, movementFeedRate, extruderNumber, config)
|
||||
return new JogControls.ExtrudeButton(printer, label, movementFeedRate, extruderNumber, theme)
|
||||
{
|
||||
BackgroundColor = config.MinimalShade,
|
||||
BorderColor = config.BorderColor40,
|
||||
BackgroundColor = theme.MinimalShade,
|
||||
BorderColor = theme.BorderColor40,
|
||||
BackgroundOutlineWidth = 1,
|
||||
VAnchor = VAnchor.Absolute,
|
||||
HAnchor = HAnchor.Absolute,
|
||||
|
|
@ -132,7 +132,7 @@ namespace MatterHackers.MatterControl
|
|||
};
|
||||
}
|
||||
|
||||
public static FlowLayoutWidget CreateMenuItems(this ThemeConfig config, PopupMenu popupMenu, IEnumerable<NamedAction> menuActions)
|
||||
public static FlowLayoutWidget CreateMenuItems(this ThemeConfig theme, PopupMenu popupMenu, IEnumerable<NamedAction> menuActions)
|
||||
{
|
||||
// Create menu items in the DropList for each element in this.menuActions
|
||||
foreach (var menuAction in menuActions)
|
||||
|
|
@ -150,7 +150,7 @@ namespace MatterHackers.MatterControl
|
|||
HAnchor = HAnchor.Fit | HAnchor.Stretch
|
||||
};
|
||||
|
||||
var textWidget = new TextWidget(menuAction.Title, pointSize: config.DefaultFontSize, textColor: config.TextColor)
|
||||
var textWidget = new TextWidget(menuAction.Title, pointSize: theme.DefaultFontSize, textColor: theme.TextColor)
|
||||
{
|
||||
// Padding = MenuPadding,
|
||||
VAnchor = VAnchor.Center
|
||||
|
|
@ -161,11 +161,11 @@ namespace MatterHackers.MatterControl
|
|||
|
||||
foreach (var actionButton in namedActionButtons.Group)
|
||||
{
|
||||
var button = new TextButton(actionButton.Title, config)
|
||||
var button = new TextButton(actionButton.Title, theme)
|
||||
{
|
||||
Border = new BorderDouble(1, 0, 0, 0),
|
||||
BorderColor = config.MinimalShade,
|
||||
HoverColor = config.AccentMimimalOverlay,
|
||||
BorderColor = theme.MinimalShade,
|
||||
HoverColor = theme.AccentMimimalOverlay,
|
||||
Enabled = actionButton.IsEnabled()
|
||||
};
|
||||
|
||||
|
|
@ -181,7 +181,7 @@ namespace MatterHackers.MatterControl
|
|||
}
|
||||
}
|
||||
|
||||
var menuItem = new PopupMenu.MenuItem(content, config)
|
||||
var menuItem = new PopupMenu.MenuItem(content, theme)
|
||||
{
|
||||
HAnchor = HAnchor.Fit | HAnchor.Stretch,
|
||||
VAnchor = VAnchor.Fit,
|
||||
|
|
@ -227,18 +227,18 @@ namespace MatterHackers.MatterControl
|
|||
return popupMenu;
|
||||
}
|
||||
|
||||
public static RadioTextButton CreateMicroRadioButton(this ThemeConfig config, string text, IList<GuiWidget> siblingRadioButtonList = null)
|
||||
public static RadioTextButton CreateMicroRadioButton(this ThemeConfig theme, string text, IList<GuiWidget> siblingRadioButtonList = null)
|
||||
{
|
||||
var radioButton = new RadioTextButton(text, config, config.FontSize8)
|
||||
var radioButton = new RadioTextButton(text, theme, theme.FontSize8)
|
||||
{
|
||||
SiblingRadioButtonList = siblingRadioButtonList,
|
||||
Padding = new BorderDouble(5, 0),
|
||||
SelectedBackgroundColor = config.SlightShade,
|
||||
UnselectedBackgroundColor = config.SlightShade,
|
||||
HoverColor = config.AccentMimimalOverlay,
|
||||
SelectedBackgroundColor = theme.SlightShade,
|
||||
UnselectedBackgroundColor = theme.SlightShade,
|
||||
HoverColor = theme.AccentMimimalOverlay,
|
||||
Margin = new BorderDouble(right: 1),
|
||||
HAnchor = HAnchor.Absolute,
|
||||
Height = config.MicroButtonHeight,
|
||||
Height = theme.MicroButtonHeight,
|
||||
Width = MicroButtonWidth
|
||||
};
|
||||
|
||||
|
|
@ -248,12 +248,12 @@ namespace MatterHackers.MatterControl
|
|||
return radioButton;
|
||||
}
|
||||
|
||||
public static JogControls.MoveButton CreateMoveButton(this ThemeConfig config, PrinterConfig printer, string label, PrinterConnection.Axis axis, double movementFeedRate, bool levelingButtons = false)
|
||||
public static JogControls.MoveButton CreateMoveButton(this ThemeConfig theme, PrinterConfig printer, string label, PrinterConnection.Axis axis, double movementFeedRate, bool levelingButtons = false)
|
||||
{
|
||||
return new JogControls.MoveButton(label, printer, axis, movementFeedRate, config)
|
||||
return new JogControls.MoveButton(label, printer, axis, movementFeedRate, theme)
|
||||
{
|
||||
BackgroundColor = config.MinimalShade,
|
||||
BorderColor = config.BorderColor40,
|
||||
BackgroundColor = theme.MinimalShade,
|
||||
BorderColor = theme.BorderColor40,
|
||||
BackgroundOutlineWidth = 1,
|
||||
VAnchor = VAnchor.Absolute,
|
||||
HAnchor = HAnchor.Absolute,
|
||||
|
|
@ -264,31 +264,31 @@ namespace MatterHackers.MatterControl
|
|||
};
|
||||
}
|
||||
|
||||
public static GuiWidget CreateSearchButton(this ThemeConfig config)
|
||||
public static GuiWidget CreateSearchButton(this ThemeConfig theme)
|
||||
{
|
||||
return new IconButton(StaticData.Instance.LoadIcon("icon_search_24x24.png", 16, 16).SetToColor(config.TextColor), config)
|
||||
return new IconButton(StaticData.Instance.LoadIcon("icon_search_24x24.png", 16, 16).SetToColor(theme.TextColor), theme)
|
||||
{
|
||||
ToolTipText = "Search".Localize(),
|
||||
};
|
||||
}
|
||||
|
||||
public static GuiWidget CreateSmallResetButton(this ThemeConfig config)
|
||||
public static GuiWidget CreateSmallResetButton(this ThemeConfig theme)
|
||||
{
|
||||
return new HoverImageWidget(config.RestoreNormal, config.RestoreHover)
|
||||
return new HoverImageWidget(theme.RestoreNormal, theme.RestoreHover)
|
||||
{
|
||||
VAnchor = VAnchor.Center,
|
||||
Margin = new BorderDouble(0, 0, 5, 0)
|
||||
};
|
||||
}
|
||||
|
||||
public static PopupMenuButton CreateSplitButton(this ThemeConfig config, SplitButtonParams buttonParams, OperationGroup operationGroup = null)
|
||||
public static PopupMenuButton CreateSplitButton(this ThemeConfig theme, SplitButtonParams buttonParams, OperationGroup operationGroup = null)
|
||||
{
|
||||
PopupMenuButton menuButton = null;
|
||||
|
||||
GuiWidget innerButton;
|
||||
if (buttonParams.ButtonText == null)
|
||||
{
|
||||
innerButton = new IconButton(buttonParams.Icon, config)
|
||||
innerButton = new IconButton(buttonParams.Icon, theme)
|
||||
{
|
||||
Name = buttonParams.ButtonName + " Inner SplitButton",
|
||||
Enabled = buttonParams.ButtonEnabled,
|
||||
|
|
@ -302,7 +302,7 @@ namespace MatterHackers.MatterControl
|
|||
{
|
||||
if (buttonParams.Icon == null)
|
||||
{
|
||||
innerButton = new TextButton(buttonParams.ButtonText, config)
|
||||
innerButton = new TextButton(buttonParams.ButtonText, theme)
|
||||
{
|
||||
Name = buttonParams.ButtonName,
|
||||
Enabled = buttonParams.ButtonEnabled,
|
||||
|
|
@ -311,7 +311,7 @@ namespace MatterHackers.MatterControl
|
|||
}
|
||||
else
|
||||
{
|
||||
innerButton = new TextIconButton(buttonParams.ButtonText, buttonParams.Icon, config)
|
||||
innerButton = new TextIconButton(buttonParams.ButtonText, buttonParams.Icon, theme)
|
||||
{
|
||||
Name = buttonParams.ButtonName,
|
||||
Enabled = buttonParams.ButtonEnabled,
|
||||
|
|
@ -328,14 +328,13 @@ namespace MatterHackers.MatterControl
|
|||
|
||||
if (operationGroup == null)
|
||||
{
|
||||
menuButton = new PopupMenuButton(innerButton, config);
|
||||
menuButton = new PopupMenuButton(innerButton, theme);
|
||||
}
|
||||
else
|
||||
{
|
||||
menuButton = new OperationGroupButton(operationGroup, innerButton, config);
|
||||
menuButton = new OperationGroupButton(operationGroup, innerButton, theme);
|
||||
}
|
||||
|
||||
var theme = ApplicationController.Instance.MenuTheme;
|
||||
menuButton.DynamicPopupContent = () =>
|
||||
{
|
||||
var popupMenu = new PopupMenu(theme);
|
||||
|
|
@ -348,13 +347,13 @@ namespace MatterHackers.MatterControl
|
|||
menuButton.BackgroundColor = buttonParams.BackgroundColor;
|
||||
if (menuButton.BackgroundColor == Color.Transparent)
|
||||
{
|
||||
menuButton.BackgroundColor = config.ToolbarButtonBackground;
|
||||
menuButton.BackgroundColor = theme.ToolbarButtonBackground;
|
||||
}
|
||||
|
||||
menuButton.HoverColor = config.ToolbarButtonHover;
|
||||
menuButton.MouseDownColor = config.ToolbarButtonDown;
|
||||
menuButton.HoverColor = theme.ToolbarButtonHover;
|
||||
menuButton.MouseDownColor = theme.ToolbarButtonDown;
|
||||
menuButton.DrawArrow = true;
|
||||
menuButton.Margin = config.ButtonSpacing;
|
||||
menuButton.Margin = theme.ButtonSpacing;
|
||||
menuButton.DistinctPopupButton = true;
|
||||
menuButton.BackgroundRadius = new RadiusCorners(theme.ButtonRadius * GuiWidget.DeviceScale, theme.ButtonRadius * GuiWidget.DeviceScale, 0, 0);
|
||||
|
||||
|
|
@ -362,12 +361,12 @@ namespace MatterHackers.MatterControl
|
|||
return menuButton;
|
||||
}
|
||||
|
||||
public static void RebuildTheme(this ThemeConfig config)
|
||||
public static void RebuildTheme(this ThemeConfig theme)
|
||||
{
|
||||
config.GeneratingThumbnailIcon = StaticData.Instance.LoadIcon("building_thumbnail_40x40.png", 40, 40).SetToColor(config.TextColor);
|
||||
theme.GeneratingThumbnailIcon = StaticData.Instance.LoadIcon("building_thumbnail_40x40.png", 40, 40).SetToColor(theme.TextColor);
|
||||
}
|
||||
|
||||
public static void RemovePrimaryActionStyle(this ThemeConfig config, GuiWidget guiWidget)
|
||||
public static void RemovePrimaryActionStyle(this ThemeConfig theme, GuiWidget guiWidget)
|
||||
{
|
||||
guiWidget.BackgroundColor = Color.Transparent;
|
||||
|
||||
|
|
@ -377,11 +376,11 @@ namespace MatterHackers.MatterControl
|
|||
switch (guiWidget)
|
||||
{
|
||||
case SimpleFlowButton flowButton:
|
||||
flowButton.HoverColor = parentIsToolbar ? config.ToolbarButtonHover : Color.Transparent;
|
||||
flowButton.HoverColor = parentIsToolbar ? theme.ToolbarButtonHover : Color.Transparent;
|
||||
break;
|
||||
|
||||
case SimpleButton button:
|
||||
button.HoverColor = parentIsToolbar ? config.ToolbarButtonHover : Color.Transparent;
|
||||
button.HoverColor = parentIsToolbar ? theme.ToolbarButtonHover : Color.Transparent;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,7 +142,6 @@ namespace MatterHackers.MatterControl.ConfigurationPage
|
|||
VAnchor = VAnchor.Absolute,
|
||||
Width = 80 * GuiWidget.DeviceScale,
|
||||
Height = 65 * GuiWidget.DeviceScale,
|
||||
Mode = themeName,
|
||||
Border = 1,
|
||||
BorderColor = theme.BorderColor20,
|
||||
Margin = new BorderDouble(theme.DefaultContainerPadding, 0, theme.DefaultContainerPadding, theme.DefaultContainerPadding)
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ namespace MatterHackers.MatterControl.ConfigurationPage
|
|||
overlay.Click += (s, e) =>
|
||||
{
|
||||
// Activate the theme
|
||||
themeColorPanel.SetThemeColor(this.ThemeSet, primaryAccentColor, this.Mode);
|
||||
themeColorPanel.SetThemeColor(this.ThemeSet, primaryAccentColor);
|
||||
|
||||
// Disable further theme clicks until reload completes
|
||||
themeColorPanel.Enabled = false;
|
||||
|
|
@ -136,8 +136,6 @@ namespace MatterHackers.MatterControl.ConfigurationPage
|
|||
|
||||
public ThemeSet ThemeSet { get; }
|
||||
|
||||
public string Mode { get; internal set; }
|
||||
|
||||
public void PreviewThemeColor(Color sourceColor)
|
||||
{
|
||||
var adjustedAccentColor = sourceColor;
|
||||
|
|
|
|||
|
|
@ -295,11 +295,11 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
}
|
||||
}
|
||||
|
||||
public void AddRightClickMenuItemsItems(PopupMenu popupMenu)
|
||||
public void AddRightClickMenuItemsItems(PopupMenu popupMenu, ThemeConfig theme)
|
||||
{
|
||||
popupMenu.CreateSeparator();
|
||||
popupMenu.CreateSeparator();
|
||||
|
||||
string componentID = this.ComponentID;
|
||||
string componentID = this.ComponentID;
|
||||
|
||||
var helpItem = popupMenu.CreateMenuItem("Help".Localize());
|
||||
var helpArticlesByID = ApplicationController.Instance.HelpArticlesByID;
|
||||
|
|
@ -316,5 +316,10 @@ namespace MatterHackers.MatterControl.DesignTools
|
|||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRightClickMenuItemsItems(PopupMenu popupMenu)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -234,14 +234,14 @@ namespace MatterHackers.MatterControl.Library.Widgets
|
|||
viewOptionsButton.DynamicPopupContent = () =>
|
||||
{
|
||||
var popupMenu = new PopupMenu(theme);
|
||||
CreateSortingMenu(popupMenu, libraryView);
|
||||
CreateSortingMenu(popupMenu, theme, libraryView);
|
||||
return popupMenu;
|
||||
};
|
||||
|
||||
return viewOptionsButton;
|
||||
}
|
||||
|
||||
public static PopupMenu CreateSortingMenu(PopupMenu popupMenu, LibraryListView libraryView)
|
||||
public static PopupMenu CreateSortingMenu(PopupMenu popupMenu, ThemeConfig theme, LibraryListView libraryView)
|
||||
{
|
||||
var siblingList = new List<GuiWidget>();
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ namespace MatterHackers.MatterControl.Library.Widgets
|
|||
toolbar.OverflowButton.Name = "Print Library View Options";
|
||||
toolbar.Padding = theme.ToolbarPadding;
|
||||
|
||||
toolbar.ExtendOverflowMenu = (popupMenu) => LibraryWidget.CreateSortingMenu(popupMenu, libraryView);
|
||||
toolbar.ExtendOverflowMenu = (popupMenu) => LibraryWidget.CreateSortingMenu(popupMenu, theme, libraryView);
|
||||
|
||||
allControls.AddChild(toolbar);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2018, Lars Brubaker, John Lewin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those
|
||||
of the authors and should not be interpreted as representing official policies,
|
||||
either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
|
||||
|
||||
namespace MatterHackers.MatterControl.PartPreviewWindow
|
||||
{
|
||||
public enum ArrowDirection
|
||||
{
|
||||
Right,
|
||||
Left,
|
||||
Up,
|
||||
Down,
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -115,6 +115,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
popupContent.Closed += PopupContent_Closed;
|
||||
|
||||
systemWindow.ShowPopup(
|
||||
theme,
|
||||
this.AnchorMate,
|
||||
this.PopupMate,
|
||||
this.AltPopupBounds);
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
|
||||
var systemWindow = this.Parents<SystemWindow>().FirstOrDefault();
|
||||
systemWindow.ShowRightSplitPopup(
|
||||
theme,
|
||||
new MatePoint(searchButton),
|
||||
new MatePoint(searchPanel),
|
||||
borderWidth: 0);
|
||||
|
|
@ -700,6 +701,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
|
||||
var systemWindow = this.Parents<SystemWindow>().FirstOrDefault();
|
||||
systemWindow.ShowPopup(
|
||||
theme,
|
||||
new MatePoint(themePanel)
|
||||
{
|
||||
Mate = new MateOptions(MateEdge.Right, MateEdge.Top),
|
||||
|
|
|
|||
|
|
@ -1,654 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017, Lars Brubaker, John Lewin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those
|
||||
of the authors and should not be interpreted as representing official policies,
|
||||
either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MatterHackers.Agg;
|
||||
using MatterHackers.Agg.Image;
|
||||
using MatterHackers.Agg.Platform;
|
||||
using MatterHackers.Agg.UI;
|
||||
using MatterHackers.Agg.VertexSource;
|
||||
using MatterHackers.ImageProcessing;
|
||||
using MatterHackers.MatterControl.CustomWidgets;
|
||||
using MatterHackers.MatterControl.SlicerConfiguration;
|
||||
using MatterHackers.VectorMath;
|
||||
|
||||
namespace MatterHackers.MatterControl.PartPreviewWindow
|
||||
{
|
||||
public class PopupMenu : FlowLayoutWidget, IIgnoredPopupChild
|
||||
{
|
||||
private ThemeConfig theme;
|
||||
|
||||
public static BorderDouble MenuPadding => new BorderDouble(40, 8, 20, 8);
|
||||
|
||||
public static Color DisabledTextColor { get; set; } = Color.Gray;
|
||||
|
||||
public PopupMenu(ThemeConfig theme)
|
||||
: base(FlowDirection.TopToBottom)
|
||||
{
|
||||
this.theme = theme;
|
||||
this.VAnchor = VAnchor.Fit;
|
||||
this.HAnchor = HAnchor.Fit;
|
||||
this.BackgroundColor = theme.BackgroundColor;
|
||||
}
|
||||
|
||||
public HorizontalLine CreateSeparator(double height = 1)
|
||||
{
|
||||
var line = new HorizontalLine(ApplicationController.Instance.MenuTheme.BorderColor20)
|
||||
{
|
||||
Margin = new BorderDouble(8, 1),
|
||||
BackgroundColor = theme.RowBorder,
|
||||
Height = height * DeviceScale,
|
||||
};
|
||||
|
||||
this.AddChild(line);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
public MenuItem CreateMenuItem(string name, ImageBuffer icon = null, string shortCut = null)
|
||||
{
|
||||
GuiWidget content;
|
||||
|
||||
var textWidget = new TextWidget(name, pointSize: theme.DefaultFontSize, textColor: theme.TextColor)
|
||||
{
|
||||
Padding = MenuPadding,
|
||||
};
|
||||
|
||||
if (shortCut != null)
|
||||
{
|
||||
content = new GuiWidget()
|
||||
{
|
||||
HAnchor = HAnchor.Stretch,
|
||||
VAnchor = VAnchor.Fit
|
||||
};
|
||||
|
||||
content.AddChild(new TextWidget(shortCut, pointSize: theme.DefaultFontSize, textColor: theme.TextColor)
|
||||
{
|
||||
HAnchor = HAnchor.Right
|
||||
});
|
||||
|
||||
content.AddChild(textWidget);
|
||||
}
|
||||
else
|
||||
{
|
||||
content = textWidget;
|
||||
}
|
||||
|
||||
content.Selectable = false;
|
||||
|
||||
var menuItem = new MenuItem(content, theme)
|
||||
{
|
||||
Name = name + " Menu Item",
|
||||
Image = icon
|
||||
};
|
||||
|
||||
menuItem.Click += (s, e) =>
|
||||
{
|
||||
Unfocus();
|
||||
};
|
||||
|
||||
this.AddChild(menuItem);
|
||||
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
public class SubMenuItemButton : MenuItem, IIgnoredPopupChild
|
||||
{
|
||||
public PopupMenu SubMenu { get; set; }
|
||||
|
||||
public SubMenuItemButton(GuiWidget content, ThemeConfig theme) : base(content, theme)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnDraw(Graphics2D graphics2D)
|
||||
{
|
||||
base.OnDraw(graphics2D);
|
||||
|
||||
// draw the right arrow
|
||||
var x = this.LocalBounds.Right - this.LocalBounds.Height / 2;
|
||||
var y = this.Size.Y / 2 + 2;
|
||||
|
||||
var arrow = new VertexStorage();
|
||||
arrow.MoveTo(x + 3, y);
|
||||
arrow.LineTo(x - 3, y + 5);
|
||||
arrow.LineTo(x - 3, y - 5);
|
||||
|
||||
graphics2D.Render(arrow, theme.TextColor);
|
||||
}
|
||||
|
||||
public bool KeepMenuOpen
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SubMenu != null)
|
||||
{
|
||||
return SubMenu.ContainsFocus;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CheckboxMenuItem : MenuItem, IIgnoredPopupChild, ICheckbox
|
||||
{
|
||||
private bool _checked;
|
||||
|
||||
private ImageBuffer faChecked;
|
||||
|
||||
public CheckboxMenuItem(GuiWidget widget, ThemeConfig theme)
|
||||
: base(widget, theme)
|
||||
{
|
||||
faChecked = StaticData.Instance.LoadIcon("fa-check_16.png", 16, 16).SetToColor(theme.TextColor);
|
||||
}
|
||||
|
||||
public override void OnLoad(EventArgs args)
|
||||
{
|
||||
this.Image = _checked ? faChecked : null;
|
||||
base.OnLoad(args);
|
||||
}
|
||||
|
||||
public bool KeepMenuOpen => false;
|
||||
|
||||
public bool Checked
|
||||
{
|
||||
get => _checked;
|
||||
set
|
||||
{
|
||||
if (_checked != value)
|
||||
{
|
||||
_checked = value;
|
||||
this.Image = _checked ? faChecked : null;
|
||||
|
||||
this.CheckedStateChanged?.Invoke(this, null);
|
||||
this.Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler CheckedStateChanged;
|
||||
}
|
||||
|
||||
public class RadioMenuItem : MenuItem, IIgnoredPopupChild, IRadioButton
|
||||
{
|
||||
private bool _checked;
|
||||
|
||||
private ImageBuffer radioIconChecked;
|
||||
|
||||
private ImageBuffer radioIconUnchecked;
|
||||
|
||||
public RadioMenuItem(GuiWidget widget, ThemeConfig theme)
|
||||
: base(widget, theme)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnLoad(EventArgs args)
|
||||
{
|
||||
// Init static radio icons if null
|
||||
if (radioIconChecked == null)
|
||||
{
|
||||
var size = (int)Math.Round(16 * GuiWidget.DeviceScale);
|
||||
radioIconChecked = new ImageBuffer(size, size).SetPreMultiply();
|
||||
radioIconUnchecked = new ImageBuffer(size, size).SetPreMultiply();
|
||||
|
||||
var rect = new RectangleDouble(0, 0, size, size);
|
||||
|
||||
RadioImage.DrawCircle(
|
||||
radioIconChecked.NewGraphics2D(),
|
||||
rect.Center,
|
||||
theme.TextColor,
|
||||
isChecked: true,
|
||||
isActive: false);
|
||||
|
||||
RadioImage.DrawCircle(
|
||||
radioIconUnchecked.NewGraphics2D(),
|
||||
rect.Center,
|
||||
theme.TextColor,
|
||||
isChecked: false,
|
||||
isActive: false);
|
||||
}
|
||||
|
||||
this.Image = _checked ? radioIconChecked : radioIconUnchecked;
|
||||
|
||||
this.Invalidate();
|
||||
|
||||
if (!this.SiblingRadioButtonList.Contains(this))
|
||||
{
|
||||
this.SiblingRadioButtonList.Add(this);
|
||||
}
|
||||
|
||||
base.OnLoad(args);
|
||||
}
|
||||
|
||||
public bool KeepMenuOpen => false;
|
||||
|
||||
public IList<GuiWidget> SiblingRadioButtonList { get; set; }
|
||||
|
||||
public bool Checked
|
||||
{
|
||||
get => _checked;
|
||||
set
|
||||
{
|
||||
if (_checked != value)
|
||||
{
|
||||
_checked = value;
|
||||
|
||||
this.Image = _checked ? radioIconChecked : radioIconUnchecked;
|
||||
|
||||
if (_checked)
|
||||
{
|
||||
this.UncheckSiblings();
|
||||
}
|
||||
|
||||
this.CheckedStateChanged?.Invoke(this, null);
|
||||
|
||||
this.Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler CheckedStateChanged;
|
||||
}
|
||||
|
||||
public void CreateSubMenu(string menuTitle, ThemeConfig menuTheme, Action<PopupMenu> populateSubMenu, ImageBuffer icon = null)
|
||||
{
|
||||
var content = new TextWidget(menuTitle, pointSize: theme.DefaultFontSize, textColor: theme.TextColor)
|
||||
{
|
||||
Padding = MenuPadding,
|
||||
};
|
||||
|
||||
content.Selectable = false;
|
||||
|
||||
var subMenuItemButton = new SubMenuItemButton(content, theme)
|
||||
{
|
||||
Name = menuTitle + " Menu Item",
|
||||
Image = icon
|
||||
};
|
||||
|
||||
this.AddChild(subMenuItemButton);
|
||||
|
||||
subMenuItemButton.Click += (s, e) =>
|
||||
{
|
||||
var systemWindow = this.Parents<SystemWindow>().FirstOrDefault();
|
||||
if (systemWindow == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var subMenu = new PopupMenu(menuTheme);
|
||||
subMenuItemButton.SubMenu = subMenu;
|
||||
|
||||
UiThread.RunOnIdle(() =>
|
||||
{
|
||||
populateSubMenu(subMenu);
|
||||
|
||||
systemWindow.ShowPopup(
|
||||
new MatePoint(subMenuItemButton)
|
||||
{
|
||||
Mate = new MateOptions(MateEdge.Right, MateEdge.Top),
|
||||
AltMate = new MateOptions(MateEdge.Left, MateEdge.Bottom)
|
||||
},
|
||||
new MatePoint(subMenu)
|
||||
{
|
||||
Mate = new MateOptions(MateEdge.Left, MateEdge.Top),
|
||||
AltMate = new MateOptions(MateEdge.Right, MateEdge.Bottom)
|
||||
});
|
||||
});
|
||||
|
||||
subMenu.Closed += (s1, e1) =>
|
||||
{
|
||||
subMenu.ClearRemovedFlag();
|
||||
subMenuItemButton.SubMenu = null;
|
||||
if (!this.ContainsFocus)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
public MenuItem CreateBoolMenuItem(string name, Func<bool> getter, Action<bool> setter, bool useRadioStyle = false, IList<GuiWidget> siblingRadioButtonList = null)
|
||||
{
|
||||
var textWidget = new TextWidget(name, pointSize: theme.DefaultFontSize, textColor: theme.TextColor)
|
||||
{
|
||||
Padding = MenuPadding,
|
||||
};
|
||||
|
||||
return this.CreateBoolMenuItem(textWidget, name, getter, setter, useRadioStyle, siblingRadioButtonList);
|
||||
}
|
||||
|
||||
public MenuItem CreateBoolMenuItem(string name, ImageBuffer icon, Func<bool> getter, Action<bool> setter, bool useRadioStyle = false, IList<GuiWidget> siblingRadioButtonList = null)
|
||||
{
|
||||
var row = new FlowLayoutWidget()
|
||||
{
|
||||
Selectable = false
|
||||
};
|
||||
row.AddChild(new IconButton(icon, theme));
|
||||
|
||||
var textWidget = new TextWidget(name, pointSize: theme.DefaultFontSize, textColor: theme.TextColor)
|
||||
{
|
||||
Padding = MenuPadding,
|
||||
VAnchor = VAnchor.Center
|
||||
};
|
||||
row.AddChild(textWidget);
|
||||
|
||||
return this.CreateBoolMenuItem(row, name, getter, setter, useRadioStyle, siblingRadioButtonList);
|
||||
}
|
||||
|
||||
public MenuItem CreateBoolMenuItem(GuiWidget guiWidget, string name, Func<bool> getter, Action<bool> setter, bool useRadioStyle = false, IList<GuiWidget> siblingRadioButtonList = null)
|
||||
{
|
||||
bool isChecked = getter?.Invoke() == true;
|
||||
|
||||
MenuItem menuItem;
|
||||
|
||||
if (useRadioStyle)
|
||||
{
|
||||
menuItem = new RadioMenuItem(guiWidget, theme)
|
||||
{
|
||||
Name = name + " Menu Item",
|
||||
Checked = isChecked,
|
||||
SiblingRadioButtonList = siblingRadioButtonList
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
menuItem = new CheckboxMenuItem(guiWidget, theme)
|
||||
{
|
||||
Name = name + " Menu Item",
|
||||
Checked = isChecked
|
||||
};
|
||||
}
|
||||
|
||||
menuItem.Click += (s, e) =>
|
||||
{
|
||||
if (menuItem is RadioMenuItem radioMenu)
|
||||
{
|
||||
// Do nothing on reclick of active radio menu
|
||||
if (radioMenu.Checked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
isChecked = radioMenu.Checked = !radioMenu.Checked;
|
||||
}
|
||||
else if (menuItem is CheckboxMenuItem checkboxMenu)
|
||||
{
|
||||
isChecked = checkboxMenu.Checked = !isChecked;
|
||||
}
|
||||
|
||||
setter?.Invoke(isChecked);
|
||||
};
|
||||
|
||||
this.AddChild(menuItem);
|
||||
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and add a new menu item
|
||||
/// </summary>
|
||||
/// <param name="text">The text of the item</param>
|
||||
/// <param name="items"></param>
|
||||
/// <param name="getter"></param>
|
||||
/// <param name="setter"></param>
|
||||
/// <returns></returns>
|
||||
public MenuItem CreateButtonSelectMenuItem(string text, IEnumerable<(string key, string text)> buttonKvps, string startingValue, Action<string> setter, double minSpacerWidth = 0)
|
||||
{
|
||||
var textWidget = new TextWidget(text, pointSize: theme.DefaultFontSize, textColor: theme.TextColor)
|
||||
{
|
||||
Padding = MenuPadding,
|
||||
VAnchor = VAnchor.Center,
|
||||
};
|
||||
|
||||
return this.CreateButtonSelectMenuItem(textWidget, text, buttonKvps, startingValue, setter, minSpacerWidth);
|
||||
}
|
||||
|
||||
public MenuItem CreateButtonMenuItem(string text,
|
||||
IEnumerable<(string key, string text, EventHandler<MouseEventArgs> click)> buttonKvps,
|
||||
double minSpacerWidth = 0,
|
||||
bool bold = false)
|
||||
{
|
||||
var textWidget = new TextWidget(text, pointSize: theme.DefaultFontSize, textColor: theme.TextColor, bold: bold)
|
||||
{
|
||||
Padding = MenuPadding,
|
||||
VAnchor = VAnchor.Center,
|
||||
};
|
||||
|
||||
return this.CreateButtonMenuItem(textWidget, text, buttonKvps, minSpacerWidth);
|
||||
}
|
||||
|
||||
public MenuItem CreateButtonSelectMenuItem(string text, ImageBuffer icon, IEnumerable<(string key, string text)> buttonKvps, string startingValue, Action<string> setter)
|
||||
{
|
||||
var row = new FlowLayoutWidget()
|
||||
{
|
||||
Selectable = false
|
||||
};
|
||||
row.AddChild(new IconButton(icon, theme));
|
||||
|
||||
var textWidget = new TextWidget(text, pointSize: theme.DefaultFontSize, textColor: theme.TextColor)
|
||||
{
|
||||
Padding = MenuPadding,
|
||||
VAnchor = VAnchor.Center
|
||||
};
|
||||
row.AddChild(textWidget);
|
||||
|
||||
return this.CreateButtonSelectMenuItem(row, text, buttonKvps, startingValue, setter);
|
||||
}
|
||||
|
||||
public MenuItem CreateButtonSelectMenuItem(GuiWidget guiWidget, string name, IEnumerable<(string key, string text)> buttonKvps, string startingValue, Action<string> setter, double minSpacerWidth = 0)
|
||||
{
|
||||
var row = new FlowLayoutWidget()
|
||||
{
|
||||
HAnchor = HAnchor.MaxFitOrStretch,
|
||||
Name = name + " Menu Item",
|
||||
};
|
||||
|
||||
row.AddChild(guiWidget);
|
||||
row.AddChild(new HorizontalSpacer()
|
||||
{
|
||||
MinimumSize = new Vector2(minSpacerWidth, 0)
|
||||
}); ;
|
||||
|
||||
foreach(var buttonKvp in buttonKvps)
|
||||
{
|
||||
var localKey = buttonKvp.key;
|
||||
var button = EnumDisplayField.CreateThemedRadioButton(buttonKvp.text, buttonKvp.key, "", startingValue == buttonKvp.key, () =>
|
||||
{
|
||||
setter?.Invoke(localKey);
|
||||
}, theme);
|
||||
row.AddChild(button);
|
||||
}
|
||||
|
||||
var menuItem = new MenuItemHoldOpen(row, theme)
|
||||
{
|
||||
};
|
||||
|
||||
this.AddChild(menuItem);
|
||||
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
public MenuItem CreateButtonMenuItem(GuiWidget guiWidget, string name, IEnumerable<(string key, string text, EventHandler<MouseEventArgs> click)> buttonKvps, double minSpacerWidth = 0)
|
||||
{
|
||||
var row = new FlowLayoutWidget()
|
||||
{
|
||||
HAnchor = HAnchor.MaxFitOrStretch,
|
||||
Name = name + " Menu Item",
|
||||
};
|
||||
|
||||
row.AddChild(guiWidget);
|
||||
row.AddChild(new HorizontalSpacer()
|
||||
{
|
||||
MinimumSize = new Vector2(minSpacerWidth, 0)
|
||||
});
|
||||
|
||||
foreach (var buttonKvp in buttonKvps)
|
||||
{
|
||||
var button = EnumDisplayField.CreateThemedButton(buttonKvp.text, buttonKvp.key, "", theme);
|
||||
button.Click += buttonKvp.click;
|
||||
row.AddChild(button);
|
||||
}
|
||||
|
||||
var menuItem = new MenuItemHoldOpen(row, theme)
|
||||
{
|
||||
};
|
||||
|
||||
this.AddChild(menuItem);
|
||||
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
public MenuItem CreateMenuItem(GuiWidget guiWidget, string name, ImageBuffer icon = null)
|
||||
{
|
||||
var menuItem = new MenuItem(guiWidget, theme)
|
||||
{
|
||||
Text = name,
|
||||
Name = name + " Menu Item",
|
||||
Image = icon
|
||||
};
|
||||
|
||||
this.AddChild(menuItem);
|
||||
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
public bool KeepMenuOpen => false;
|
||||
|
||||
public class MenuItem : SimpleButton
|
||||
{
|
||||
private GuiWidget content;
|
||||
|
||||
public MenuItem(GuiWidget content, ThemeConfig theme)
|
||||
: base(theme)
|
||||
{
|
||||
// Inflate padding to match the target (MenuGutterWidth) after scale operation in assignment
|
||||
this.Padding = new BorderDouble(left: Math.Ceiling(theme.MenuGutterWidth / DeviceScale), right: 15);
|
||||
this.HAnchor = HAnchor.MaxFitOrStretch;
|
||||
this.VAnchor = VAnchor.Fit;
|
||||
this.MinimumSize = new Vector2(150 * GuiWidget.DeviceScale, theme.ButtonHeight);
|
||||
this.content = content;
|
||||
this.GutterWidth = theme.MenuGutterWidth;
|
||||
this.HoverColor = theme.AccentMimimalOverlay;
|
||||
|
||||
content.VAnchor = VAnchor.Center;
|
||||
content.HAnchor |= HAnchor.Left;
|
||||
|
||||
this.AddChild(content);
|
||||
}
|
||||
|
||||
public double GutterWidth { get; set; }
|
||||
|
||||
public ImageBuffer Image { get; set; }
|
||||
|
||||
private ImageBuffer _disabledImage;
|
||||
|
||||
public ImageBuffer DisabledImage
|
||||
{
|
||||
get
|
||||
{
|
||||
// Lazy construct on first access
|
||||
if (this.Image != null &&
|
||||
_disabledImage == null)
|
||||
{
|
||||
_disabledImage = this.Image.AjustAlpha(0.2);
|
||||
}
|
||||
|
||||
return _disabledImage;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Enabled
|
||||
{
|
||||
get => base.Enabled;
|
||||
set
|
||||
{
|
||||
if (content is TextWidget textWidget)
|
||||
{
|
||||
textWidget.Enabled = value;
|
||||
}
|
||||
|
||||
base.Enabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool KeepMenuOpen => false;
|
||||
|
||||
public override void OnDraw(Graphics2D graphics2D)
|
||||
{
|
||||
if (this.Image != null)
|
||||
{
|
||||
var x = this.LocalBounds.Left + (this.GutterWidth / 2 - this.Image.Width / 2);
|
||||
var y = this.Size.Y / 2 - this.Image.Height / 2;
|
||||
|
||||
graphics2D.Render(this.Enabled ? this.Image : this.DisabledImage, (int)x, (int)y);
|
||||
}
|
||||
|
||||
base.OnDraw(graphics2D);
|
||||
}
|
||||
}
|
||||
|
||||
public class MenuItemHoldOpen : MenuItem, IIgnoredPopupChild
|
||||
{
|
||||
public MenuItemHoldOpen(GuiWidget content, ThemeConfig theme)
|
||||
: base(content, theme)
|
||||
{
|
||||
}
|
||||
|
||||
public bool KeepMenuOpen => false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PopupMenuExtensions
|
||||
{
|
||||
public static void ShowMenu(this PopupMenu popupMenu, GuiWidget anchorWidget, MouseEventArgs mouseEvent)
|
||||
{
|
||||
popupMenu.ShowMenu(anchorWidget, mouseEvent.Position);
|
||||
}
|
||||
|
||||
public static void ShowMenu(this PopupMenu popupMenu, GuiWidget anchorWidget, Vector2 menuPosition)
|
||||
{
|
||||
var systemWindow = anchorWidget.Parents<SystemWindow>().LastOrDefault();
|
||||
systemWindow.ToolTipManager.Clear();
|
||||
systemWindow.ShowPopup(
|
||||
new MatePoint(anchorWidget)
|
||||
{
|
||||
Mate = new MateOptions(MateEdge.Left, MateEdge.Top),
|
||||
AltMate = new MateOptions(MateEdge.Left, MateEdge.Bottom)
|
||||
},
|
||||
new MatePoint(popupMenu)
|
||||
{
|
||||
Mate = new MateOptions(MateEdge.Left, MateEdge.Top),
|
||||
AltMate = new MateOptions(MateEdge.Right, MateEdge.Bottom)
|
||||
},
|
||||
altBounds: new RectangleDouble(menuPosition.X + 1, menuPosition.Y + 1, menuPosition.X + 1, menuPosition.Y + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,418 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2017, Lars Brubaker, John Lewin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those
|
||||
of the authors and should not be interpreted as representing official policies,
|
||||
either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MatterHackers.Agg;
|
||||
using MatterHackers.Agg.UI;
|
||||
using MatterHackers.Agg.VertexSource;
|
||||
using MatterHackers.DataConverters3D;
|
||||
using MatterHackers.MatterControl.CustomWidgets;
|
||||
using MatterHackers.MatterControl.SlicerConfiguration;
|
||||
using MatterHackers.VectorMath;
|
||||
|
||||
namespace MatterHackers.MatterControl.PartPreviewWindow
|
||||
{
|
||||
[Flags]
|
||||
public enum MateEdge
|
||||
{
|
||||
Top = 1,
|
||||
Bottom = 2,
|
||||
Left = 4,
|
||||
Right = 8
|
||||
}
|
||||
|
||||
public class MateOptions
|
||||
{
|
||||
public MateOptions(MateEdge horizontalEdge = MateEdge.Left, MateEdge verticalEdge = MateEdge.Bottom)
|
||||
{
|
||||
this.HorizontalEdge = horizontalEdge;
|
||||
this.VerticalEdge = verticalEdge;
|
||||
}
|
||||
|
||||
public MateEdge HorizontalEdge { get; set; }
|
||||
|
||||
public MateEdge VerticalEdge { get; set; }
|
||||
|
||||
public bool Top => this.VerticalEdge.HasFlag(MateEdge.Top);
|
||||
|
||||
public bool Bottom => this.VerticalEdge.HasFlag(MateEdge.Bottom);
|
||||
|
||||
public bool Left => this.HorizontalEdge.HasFlag(MateEdge.Left);
|
||||
|
||||
public bool Right => this.HorizontalEdge.HasFlag(MateEdge.Right);
|
||||
}
|
||||
|
||||
public class MatePoint
|
||||
{
|
||||
public MateOptions Mate { get; set; } = new MateOptions();
|
||||
|
||||
public MateOptions AltMate { get; set; } = new MateOptions();
|
||||
|
||||
public GuiWidget Widget { get; set; }
|
||||
|
||||
public MatePoint()
|
||||
{
|
||||
}
|
||||
|
||||
public MatePoint(GuiWidget widget)
|
||||
{
|
||||
this.Widget = widget;
|
||||
}
|
||||
|
||||
public RectangleDouble Offset { get; set; }
|
||||
}
|
||||
|
||||
public interface IOverrideAutoClose
|
||||
{
|
||||
bool AllowAutoClose { get; }
|
||||
}
|
||||
|
||||
public static class SystemWindowExtension
|
||||
{
|
||||
public static void ShowPopover(this SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds = default(RectangleDouble), double secondsToClose = 0)
|
||||
{
|
||||
var settingsRow = anchor.Widget as SettingsRow;
|
||||
var sliceSettingsPopover = popup.Widget as ClickablePopover;
|
||||
|
||||
var hookedWidgets = new HashSet<GuiWidget>();
|
||||
|
||||
void Anchor_Closed(object sender, EventArgs e)
|
||||
{
|
||||
if (popup.Widget is IOverrideAutoClose overideAutoClose
|
||||
&& !overideAutoClose.AllowAutoClose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the owning widget closed, so should we
|
||||
popup.Widget.Close();
|
||||
|
||||
foreach (var widget in hookedWidgets)
|
||||
{
|
||||
widget.Closed -= Anchor_Closed;
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetRelativeTo_PositionChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (anchor.Widget?.Parent != null)
|
||||
{
|
||||
// Calculate left aligned screen space position (using widgetRelativeTo.parent)
|
||||
Vector2 anchorLeft = anchor.Widget.Parent.TransformToScreenSpace(anchor.Widget.Position);
|
||||
anchorLeft += new Vector2(altBounds.Left, altBounds.Bottom);
|
||||
|
||||
Vector2 popupPosition = anchorLeft;
|
||||
|
||||
var bounds = altBounds == default(RectangleDouble) ? anchor.Widget.LocalBounds : altBounds;
|
||||
|
||||
Vector2 xPosition = GetXAnchor(anchor.Mate, popup.Mate, popup.Widget, bounds);
|
||||
|
||||
Vector2 screenPosition;
|
||||
|
||||
screenPosition = anchorLeft + xPosition;
|
||||
|
||||
// Constrain
|
||||
if (screenPosition.X + popup.Widget.Width > systemWindow.Width
|
||||
|| screenPosition.X < 0)
|
||||
{
|
||||
var altXPosition = GetXAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
|
||||
|
||||
var altScreenPosition = anchorLeft + altXPosition;
|
||||
|
||||
// Prefer clipping on edge revealed by resize
|
||||
if ((popup.AltMate.Right && altScreenPosition.X > -15)
|
||||
|| (popup.AltMate.Left && altScreenPosition.X + popup.Widget.Width < systemWindow.Width))
|
||||
{
|
||||
xPosition = altXPosition;
|
||||
|
||||
if (settingsRow != null
|
||||
&& sliceSettingsPopover != null)
|
||||
{
|
||||
sliceSettingsPopover.ArrowDirection = settingsRow.ArrowDirection == ArrowDirection.Left ? ArrowDirection.Right : ArrowDirection.Left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
popupPosition += xPosition;
|
||||
|
||||
Vector2 yPosition = GetYAnchor(anchor.Mate, popup.Mate, popup.Widget, bounds);
|
||||
|
||||
screenPosition = anchorLeft + yPosition;
|
||||
|
||||
// Constrain
|
||||
if (anchor.AltMate != null
|
||||
&& (screenPosition.Y + popup.Widget.Height > systemWindow.Height
|
||||
|| screenPosition.Y < 0))
|
||||
{
|
||||
yPosition = GetYAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
|
||||
|
||||
if (settingsRow != null)
|
||||
{
|
||||
settingsRow.ArrowDirection = settingsRow.ArrowDirection == ArrowDirection.Up ? ArrowDirection.Down : ArrowDirection.Up;
|
||||
}
|
||||
}
|
||||
|
||||
popup.Widget.Closed += Anchor_Closed;
|
||||
anchor.Widget.Closed += Anchor_Closed;
|
||||
hookedWidgets.Add(anchor.Widget);
|
||||
|
||||
foreach (var widget in anchor.Widget.Parents<GuiWidget>())
|
||||
{
|
||||
widget.Closed += Anchor_Closed;
|
||||
hookedWidgets.Add(widget);
|
||||
}
|
||||
|
||||
popupPosition += yPosition;
|
||||
|
||||
popup.Widget.Position = popupPosition;
|
||||
}
|
||||
}
|
||||
|
||||
WidgetRelativeTo_PositionChanged(anchor.Widget, null);
|
||||
|
||||
popup.Widget.BoundsChanged += (s, e) => WidgetRelativeTo_PositionChanged(anchor.Widget, null);
|
||||
|
||||
// When the widgets position changes, sync the popup position
|
||||
systemWindow?.AddChild(popup.Widget);
|
||||
|
||||
if (secondsToClose > 0)
|
||||
{
|
||||
UiThread.RunOnIdle(() => Anchor_Closed(null, null), secondsToClose);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RightHorizontalSplitPopup(SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds)
|
||||
{
|
||||
// Calculate left for right aligned split
|
||||
Vector2 popupPosition = new Vector2(systemWindow.Width - popup.Widget.Width, 0);
|
||||
|
||||
Vector2 anchorLeft = anchor.Widget.Parent.TransformToScreenSpace(anchor.Widget.Position);
|
||||
|
||||
popup.Widget.Height = anchorLeft.Y;
|
||||
|
||||
popup.Widget.Position = popupPosition;
|
||||
}
|
||||
|
||||
public static void ShowPopup(this SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds = default(RectangleDouble), int borderWidth = 1)
|
||||
{
|
||||
ShowPopup(systemWindow, anchor, popup, altBounds, borderWidth, BestPopupPosition);
|
||||
}
|
||||
|
||||
public static void ShowRightSplitPopup(this SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds = default(RectangleDouble), int borderWidth = 1)
|
||||
{
|
||||
ShowPopup(systemWindow, anchor, popup, altBounds, borderWidth, RightHorizontalSplitPopup);
|
||||
}
|
||||
|
||||
public static void ShowPopup(this SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds, int borderWidth, Action<SystemWindow, MatePoint, MatePoint, RectangleDouble> layoutHelper)
|
||||
{
|
||||
var hookedParents = new HashSet<GuiWidget>();
|
||||
|
||||
List<IIgnoredPopupChild> ignoredWidgets = popup.Widget.Children.OfType<IIgnoredPopupChild>().ToList();
|
||||
|
||||
void Widget_Draw(object sender, DrawEventArgs e)
|
||||
{
|
||||
if (borderWidth > 0)
|
||||
{
|
||||
e.Graphics2D.Render(
|
||||
new Stroke(
|
||||
new RoundedRect(popup.Widget.LocalBounds, 0),
|
||||
borderWidth * 2),
|
||||
AppContext.Theme.PopupBorderColor);
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetRelativeTo_PositionChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (anchor.Widget?.Parent != null)
|
||||
{
|
||||
layoutHelper.Invoke(systemWindow, anchor, popup, altBounds);
|
||||
}
|
||||
}
|
||||
|
||||
void CloseMenu()
|
||||
{
|
||||
popup.Widget.AfterDraw -= Widget_Draw;
|
||||
|
||||
popup.Widget.Close();
|
||||
|
||||
anchor.Widget.Closed -= Anchor_Closed;
|
||||
|
||||
// Unbind callbacks on parents for position_changed if we're closing
|
||||
foreach (GuiWidget widget in hookedParents)
|
||||
{
|
||||
widget.PositionChanged -= WidgetRelativeTo_PositionChanged;
|
||||
widget.BoundsChanged -= WidgetRelativeTo_PositionChanged;
|
||||
}
|
||||
|
||||
// Long lived originating item must be unregistered
|
||||
anchor.Widget.Closed -= Anchor_Closed;
|
||||
|
||||
// Restore focus to originating widget on close
|
||||
if (anchor.Widget?.HasBeenClosed == false)
|
||||
{
|
||||
anchor.Widget.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
void FocusChanged(object s, EventArgs e)
|
||||
{
|
||||
UiThread.RunOnIdle(() =>
|
||||
{
|
||||
// Fired any time focus changes. Traditionally we closed the menu if we weren't focused.
|
||||
// To accommodate children (or external widgets) having focus we also query for and consider special cases
|
||||
bool specialChildHasFocus = ignoredWidgets.Any(w => w.ContainsFocus || w.Focused || w.KeepMenuOpen);
|
||||
bool descendantIsHoldingOpen = popup.Widget.Descendants<GuiWidget>().Any(w => w is IIgnoredPopupChild ignoredPopupChild
|
||||
&& ignoredPopupChild.KeepMenuOpen);
|
||||
|
||||
// If the focused changed and we've lost focus and no special cases permit, close the menu
|
||||
if (!popup.Widget.ContainsFocus
|
||||
&& !specialChildHasFocus
|
||||
&& !descendantIsHoldingOpen
|
||||
&& !PopupWidget.DebugKeepOpen)
|
||||
{
|
||||
CloseMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Anchor_Closed(object sender, EventArgs e)
|
||||
{
|
||||
// If the owning widget closed, so should we
|
||||
CloseMenu();
|
||||
}
|
||||
|
||||
foreach (var ancestor in anchor.Widget.Parents<GuiWidget>().Where(p => p != systemWindow))
|
||||
{
|
||||
if (hookedParents.Add(ancestor))
|
||||
{
|
||||
ancestor.PositionChanged += WidgetRelativeTo_PositionChanged;
|
||||
ancestor.BoundsChanged += WidgetRelativeTo_PositionChanged;
|
||||
}
|
||||
}
|
||||
|
||||
popup.Widget.ContainsFocusChanged += FocusChanged;
|
||||
popup.Widget.AfterDraw += Widget_Draw;
|
||||
|
||||
WidgetRelativeTo_PositionChanged(anchor.Widget, null);
|
||||
anchor.Widget.Closed += Anchor_Closed;
|
||||
|
||||
// When the widgets position changes, sync the popup position
|
||||
systemWindow?.AddChild(popup.Widget);
|
||||
|
||||
popup.Widget.Closed += (s, e) =>
|
||||
{
|
||||
Console.WriteLine();
|
||||
};
|
||||
|
||||
popup.Widget.Focus();
|
||||
|
||||
popup.Widget.Invalidate();
|
||||
}
|
||||
|
||||
private static void BestPopupPosition(SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds)
|
||||
{
|
||||
// Calculate left aligned screen space position (using widgetRelativeTo.parent)
|
||||
Vector2 anchorLeft = anchor.Widget.Parent.TransformToScreenSpace(anchor.Widget.Position);
|
||||
anchorLeft += new Vector2(altBounds.Left, altBounds.Bottom);
|
||||
|
||||
Vector2 popupPosition = anchorLeft;
|
||||
|
||||
var bounds = altBounds == default(RectangleDouble) ? anchor.Widget.LocalBounds : altBounds;
|
||||
|
||||
Vector2 xPosition = GetXAnchor(anchor.Mate, popup.Mate, popup.Widget, bounds);
|
||||
|
||||
Vector2 screenPosition;
|
||||
|
||||
screenPosition = anchorLeft + xPosition;
|
||||
|
||||
// Constrain
|
||||
if (screenPosition.X + popup.Widget.Width > systemWindow.Width
|
||||
|| screenPosition.X < 0)
|
||||
{
|
||||
xPosition = GetXAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
|
||||
}
|
||||
|
||||
popupPosition += xPosition;
|
||||
|
||||
Vector2 yPosition = GetYAnchor(anchor.Mate, popup.Mate, popup.Widget, bounds);
|
||||
|
||||
screenPosition = anchorLeft + yPosition;
|
||||
|
||||
// Constrain
|
||||
if (anchor.AltMate != null
|
||||
&& (screenPosition.Y + popup.Widget.Height > systemWindow.Height
|
||||
|| screenPosition.Y < 0))
|
||||
{
|
||||
yPosition = GetYAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
|
||||
}
|
||||
|
||||
popupPosition += yPosition;
|
||||
|
||||
popup.Widget.Position = popupPosition;
|
||||
}
|
||||
|
||||
private static Vector2 GetYAnchor(MateOptions anchor, MateOptions popup, GuiWidget popupWidget, RectangleDouble bounds)
|
||||
{
|
||||
if (anchor.Top && popup.Bottom)
|
||||
{
|
||||
return new Vector2(0, bounds.Height);
|
||||
}
|
||||
else if (anchor.Top && popup.Top)
|
||||
{
|
||||
return new Vector2(0, popupWidget.Height - bounds.Height) * -1;
|
||||
}
|
||||
else if (anchor.Bottom && popup.Top)
|
||||
{
|
||||
return new Vector2(0, -popupWidget.Height);
|
||||
}
|
||||
|
||||
return Vector2.Zero;
|
||||
}
|
||||
|
||||
private static Vector2 GetXAnchor(MateOptions anchor, MateOptions popup, GuiWidget popupWidget, RectangleDouble bounds)
|
||||
{
|
||||
if (anchor.Right && popup.Left)
|
||||
{
|
||||
return new Vector2(bounds.Width, 0);
|
||||
}
|
||||
else if (anchor.Left && popup.Right)
|
||||
{
|
||||
return new Vector2(-popupWidget.Width, 0);
|
||||
}
|
||||
else if (anchor.Right && popup.Right)
|
||||
{
|
||||
return new Vector2(popupWidget.Width - bounds.Width, 0) * -1;
|
||||
}
|
||||
|
||||
return Vector2.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2074,6 +2074,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
|
||||
var systemWindow = this.Parents<SystemWindow>().FirstOrDefault();
|
||||
systemWindow.ShowPopup(
|
||||
theme,
|
||||
new MatePoint(this)
|
||||
{
|
||||
Mate = new MateOptions(MateEdge.Left, MateEdge.Bottom),
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ either expressed or implied, of the FreeBSD Project.
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Markdig.Agg;
|
||||
using MatterHackers.Agg;
|
||||
|
|
@ -40,7 +39,7 @@ using MatterHackers.VectorMath;
|
|||
|
||||
namespace MatterHackers.MatterControl.CustomWidgets
|
||||
{
|
||||
public class SettingsRow : FlowLayoutWidget
|
||||
public class SettingsRow : FlowLayoutWidget
|
||||
{
|
||||
protected GuiWidget overrideIndicator;
|
||||
protected const bool debugLayout = false;
|
||||
|
|
|
|||
153
MatterControlLib/SlicerConfiguration/SettingsRowExtensions.cs
Normal file
153
MatterControlLib/SlicerConfiguration/SettingsRowExtensions.cs
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
Copyright (c) 2022, Lars Brubaker, John Lewin
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those
|
||||
of the authors and should not be interpreted as representing official policies,
|
||||
either expressed or implied, of the FreeBSD Project.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MatterHackers.Agg;
|
||||
using MatterHackers.Agg.UI;
|
||||
using MatterHackers.MatterControl.PartPreviewWindow;
|
||||
using MatterHackers.VectorMath;
|
||||
|
||||
namespace MatterHackers.MatterControl.CustomWidgets
|
||||
{
|
||||
public static class SettingsRowExtensions
|
||||
{
|
||||
public static void ShowPopover(this SystemWindow systemWindow, MatePoint anchor, MatePoint popup, RectangleDouble altBounds = default(RectangleDouble), double secondsToClose = 0)
|
||||
{
|
||||
var settingsRow = anchor.Widget as SettingsRow;
|
||||
var sliceSettingsPopover = popup.Widget as ClickablePopover;
|
||||
|
||||
var hookedWidgets = new HashSet<GuiWidget>();
|
||||
|
||||
void Anchor_Closed(object sender, EventArgs e)
|
||||
{
|
||||
if (popup.Widget is IOverrideAutoClose overideAutoClose
|
||||
&& !overideAutoClose.AllowAutoClose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the owning widget closed, so should we
|
||||
popup.Widget.Close();
|
||||
|
||||
foreach (var widget in hookedWidgets)
|
||||
{
|
||||
widget.Closed -= Anchor_Closed;
|
||||
}
|
||||
}
|
||||
|
||||
void WidgetRelativeTo_PositionChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (anchor.Widget?.Parent != null)
|
||||
{
|
||||
// Calculate left aligned screen space position (using widgetRelativeTo.parent)
|
||||
Vector2 anchorLeft = anchor.Widget.Parent.TransformToScreenSpace(anchor.Widget.Position);
|
||||
anchorLeft += new Vector2(altBounds.Left, altBounds.Bottom);
|
||||
|
||||
Vector2 popupPosition = anchorLeft;
|
||||
|
||||
var bounds = altBounds == default(RectangleDouble) ? anchor.Widget.LocalBounds : altBounds;
|
||||
|
||||
Vector2 xPosition = PopupMenu.GetXAnchor(anchor.Mate, popup.Mate, popup.Widget, bounds);
|
||||
|
||||
Vector2 screenPosition;
|
||||
|
||||
screenPosition = anchorLeft + xPosition;
|
||||
|
||||
// Constrain
|
||||
if (screenPosition.X + popup.Widget.Width > systemWindow.Width
|
||||
|| screenPosition.X < 0)
|
||||
{
|
||||
var altXPosition = PopupMenu.GetXAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
|
||||
|
||||
var altScreenPosition = anchorLeft + altXPosition;
|
||||
|
||||
// Prefer clipping on edge revealed by resize
|
||||
if ((popup.AltMate.Right && altScreenPosition.X > -15)
|
||||
|| (popup.AltMate.Left && altScreenPosition.X + popup.Widget.Width < systemWindow.Width))
|
||||
{
|
||||
xPosition = altXPosition;
|
||||
|
||||
if (settingsRow != null
|
||||
&& sliceSettingsPopover != null)
|
||||
{
|
||||
sliceSettingsPopover.ArrowDirection = settingsRow.ArrowDirection == ArrowDirection.Left ? ArrowDirection.Right : ArrowDirection.Left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
popupPosition += xPosition;
|
||||
|
||||
Vector2 yPosition = PopupMenu.GetYAnchor(anchor.Mate, popup.Mate, popup.Widget, bounds);
|
||||
|
||||
screenPosition = anchorLeft + yPosition;
|
||||
|
||||
// Constrain
|
||||
if (anchor.AltMate != null
|
||||
&& (screenPosition.Y + popup.Widget.Height > systemWindow.Height
|
||||
|| screenPosition.Y < 0))
|
||||
{
|
||||
yPosition = PopupMenu.GetYAnchor(anchor.AltMate, popup.AltMate, popup.Widget, bounds);
|
||||
|
||||
if (settingsRow != null)
|
||||
{
|
||||
settingsRow.ArrowDirection = settingsRow.ArrowDirection == ArrowDirection.Up ? ArrowDirection.Down : ArrowDirection.Up;
|
||||
}
|
||||
}
|
||||
|
||||
popup.Widget.Closed += Anchor_Closed;
|
||||
anchor.Widget.Closed += Anchor_Closed;
|
||||
hookedWidgets.Add(anchor.Widget);
|
||||
|
||||
foreach (var widget in anchor.Widget.Parents<GuiWidget>())
|
||||
{
|
||||
widget.Closed += Anchor_Closed;
|
||||
hookedWidgets.Add(widget);
|
||||
}
|
||||
|
||||
popupPosition += yPosition;
|
||||
|
||||
popup.Widget.Position = popupPosition;
|
||||
}
|
||||
}
|
||||
|
||||
WidgetRelativeTo_PositionChanged(anchor.Widget, null);
|
||||
|
||||
popup.Widget.BoundsChanged += (s, e) => WidgetRelativeTo_PositionChanged(anchor.Widget, null);
|
||||
|
||||
// When the widgets position changes, sync the popup position
|
||||
systemWindow?.AddChild(popup.Widget);
|
||||
|
||||
if (secondsToClose > 0)
|
||||
{
|
||||
UiThread.RunOnIdle(() => Anchor_Closed(null, null), secondsToClose);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -375,4 +375,120 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
|
|||
base.OnValueChanged(fieldChangedEventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MenuExtensions
|
||||
{
|
||||
public static MenuItem CreateButtonSelectMenuItem(this PopupMenu popuMenu, GuiWidget guiWidget, string name, IEnumerable<(string key, string text)> buttonKvps, string startingValue, Action<string> setter, double minSpacerWidth = 0)
|
||||
{
|
||||
var row = new FlowLayoutWidget()
|
||||
{
|
||||
HAnchor = HAnchor.MaxFitOrStretch,
|
||||
Name = name + " Menu Item",
|
||||
};
|
||||
|
||||
row.AddChild(guiWidget);
|
||||
row.AddChild(new HorizontalSpacer()
|
||||
{
|
||||
MinimumSize = new Vector2(minSpacerWidth, 0)
|
||||
}); ;
|
||||
|
||||
foreach (var buttonKvp in buttonKvps)
|
||||
{
|
||||
var localKey = buttonKvp.key;
|
||||
var button = EnumDisplayField.CreateThemedRadioButton(buttonKvp.text, buttonKvp.key, "", startingValue == buttonKvp.key, () =>
|
||||
{
|
||||
setter?.Invoke(localKey);
|
||||
}, popuMenu.Theme);
|
||||
row.AddChild(button);
|
||||
}
|
||||
|
||||
MenuItem menuItem = new MenuItemHoldOpen(row);
|
||||
|
||||
popuMenu.AddChild(menuItem);
|
||||
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
public static MenuItem CreateButtonMenuItem(this PopupMenu popupMenu, GuiWidget guiWidget, string name, IEnumerable<(string key, string text, EventHandler<MouseEventArgs> click)> buttonKvps, double minSpacerWidth = 0)
|
||||
{
|
||||
var row = new FlowLayoutWidget()
|
||||
{
|
||||
HAnchor = HAnchor.MaxFitOrStretch,
|
||||
Name = name + " Menu Item",
|
||||
};
|
||||
|
||||
row.AddChild(guiWidget);
|
||||
row.AddChild(new HorizontalSpacer()
|
||||
{
|
||||
MinimumSize = new Vector2(minSpacerWidth, 0)
|
||||
});
|
||||
|
||||
foreach (var buttonKvp in buttonKvps)
|
||||
{
|
||||
var button = EnumDisplayField.CreateThemedButton(buttonKvp.text, buttonKvp.key, "", popupMenu.Theme);
|
||||
button.Click += buttonKvp.click;
|
||||
row.AddChild(button);
|
||||
}
|
||||
|
||||
var menuItem = new MenuItemHoldOpen(row)
|
||||
{
|
||||
};
|
||||
|
||||
popupMenu.AddChild(menuItem);
|
||||
|
||||
return menuItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and add a new menu item
|
||||
/// </summary>
|
||||
/// <param name="text">The text of the item</param>
|
||||
/// <param name="items"></param>
|
||||
/// <param name="getter"></param>
|
||||
/// <param name="setter"></param>
|
||||
/// <returns></returns>
|
||||
public static MenuItem CreateButtonSelectMenuItem(this PopupMenu popupMenu, string text, IEnumerable<(string key, string text)> buttonKvps, string startingValue, Action<string> setter, double minSpacerWidth = 0)
|
||||
{
|
||||
var textWidget = new TextWidget(text, pointSize: popupMenu.Theme.DefaultFontSize, textColor: popupMenu.Theme.TextColor)
|
||||
{
|
||||
Padding = PopupMenu.MenuPadding,
|
||||
VAnchor = VAnchor.Center,
|
||||
};
|
||||
|
||||
return popupMenu.CreateButtonSelectMenuItem(textWidget, text, buttonKvps, startingValue, setter, minSpacerWidth);
|
||||
}
|
||||
|
||||
public static MenuItem CreateButtonMenuItem(this PopupMenu popupMenu,
|
||||
string text,
|
||||
IEnumerable<(string key, string text, EventHandler<MouseEventArgs> click)> buttonKvps,
|
||||
double minSpacerWidth = 0,
|
||||
bool bold = false)
|
||||
{
|
||||
var textWidget = new TextWidget(text, pointSize: popupMenu.Theme.DefaultFontSize, textColor: popupMenu.Theme.TextColor, bold: bold)
|
||||
{
|
||||
Padding = PopupMenu.MenuPadding,
|
||||
VAnchor = VAnchor.Center,
|
||||
};
|
||||
|
||||
return popupMenu.CreateButtonMenuItem(textWidget, text, buttonKvps, minSpacerWidth);
|
||||
}
|
||||
|
||||
public static MenuItem CreateButtonSelectMenuItem(this PopupMenu popupMenu, string text, ImageBuffer icon, IEnumerable<(string key, string text)> buttonKvps, string startingValue, Action<string> setter)
|
||||
{
|
||||
var row = new FlowLayoutWidget()
|
||||
{
|
||||
Selectable = false
|
||||
};
|
||||
row.AddChild(new ThemedIconButton(icon, popupMenu.Theme));
|
||||
|
||||
var textWidget = new TextWidget(text, pointSize: popupMenu.Theme.DefaultFontSize, textColor: popupMenu.Theme.TextColor)
|
||||
{
|
||||
Padding = PopupMenu.MenuPadding,
|
||||
VAnchor = VAnchor.Center
|
||||
};
|
||||
row.AddChild(textWidget);
|
||||
|
||||
return popupMenu.CreateButtonSelectMenuItem(row, text, buttonKvps, startingValue, setter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1282,6 +1282,9 @@ Translated:Design Apps
|
|||
English:Design Name
|
||||
Translated:Design Name
|
||||
|
||||
English:Design Tools
|
||||
Translated:Design Tools
|
||||
|
||||
English:Desktop
|
||||
Translated:Desktop
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 68c71c98b5f598f0f98f786cafbc06abb4e3915f
|
||||
Subproject commit 54748bc6d030bb65593e17b76d320884a7747eb2
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 3e6a51dc89c50b69e62613bc7922508a75a2cd42
|
||||
Subproject commit f3a1fbc748f00e0b7318909b0854a5627fa0476d
|
||||
|
|
@ -56,6 +56,7 @@ namespace MatterControl.Tests.MatterControl
|
|||
button.Click += (s, e) =>
|
||||
{
|
||||
systemWindow.ShowPopup(
|
||||
new ThemeConfig(),
|
||||
new MatePoint()
|
||||
{
|
||||
Widget = button
|
||||
|
|
@ -530,7 +531,7 @@ namespace MatterControl.Tests.MatterControl
|
|||
BorderColor = Color.LightBlue.Blend(Color.Black, 0.4)
|
||||
};
|
||||
|
||||
systemWindow.ShowPopup(anchor, popup);
|
||||
systemWindow.ShowPopup(new ThemeConfig(), anchor, popup);
|
||||
};
|
||||
|
||||
anchor.Widget = button;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue