Add support for grouping scene operations into a drop menu button
- Issue MatterHackers/MCCentral#5664 consider adding a dual align quick button
This commit is contained in:
parent
3a6868f39a
commit
cb6eb43972
6 changed files with 229 additions and 114 deletions
|
|
@ -783,53 +783,61 @@ namespace MatterHackers.MatterControl
|
|||
Icon = (invertIcon) => AggContext.StaticData.LoadIcon("lay_flat.png", 16, 16).SetPreMultiply(),
|
||||
},
|
||||
new SceneSelectionSeparator(),
|
||||
new SceneSelectionOperation()
|
||||
new OperationGroup()
|
||||
{
|
||||
OperationType = typeof(CombineObject3D_2),
|
||||
TitleResolver = () => "Combine".Localize(),
|
||||
Action = (sceneContext) => new CombineObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene),
|
||||
Icon = (invertIcon) => AggContext.StaticData.LoadIcon("combine.png").SetPreMultiply(),
|
||||
IsEnabled = (scene) =>
|
||||
StickySelection = true,
|
||||
IsEnabled = (scene) => scene.SelectedItem?.VisibleMeshes().Count() > 1,
|
||||
Operations = new List<SceneSelectionOperation>()
|
||||
{
|
||||
var selectedItem = scene.SelectedItem;
|
||||
return selectedItem != null && selectedItem.VisibleMeshes().Count() > 1;
|
||||
},
|
||||
},
|
||||
new SceneSelectionOperation()
|
||||
{
|
||||
OperationType = typeof(SubtractObject3D_2),
|
||||
TitleResolver = () => "Subtract".Localize(),
|
||||
Action = (sceneContext) => new SubtractObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene),
|
||||
Icon = (invertIcon) => AggContext.StaticData.LoadIcon("subtract.png").SetPreMultiply(),
|
||||
IsEnabled = (scene) =>
|
||||
{
|
||||
var selectedItem = scene.SelectedItem;
|
||||
return selectedItem != null && selectedItem.VisibleMeshes().Count() > 1;
|
||||
},
|
||||
},
|
||||
new SceneSelectionOperation()
|
||||
{
|
||||
OperationType = typeof(IntersectionObject3D_2),
|
||||
TitleResolver = () => "Intersect".Localize(),
|
||||
Action = (sceneContext) => new IntersectionObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene),
|
||||
Icon = (invertIcon) => AggContext.StaticData.LoadIcon("intersect.png"),
|
||||
IsEnabled = (scene) =>
|
||||
{
|
||||
var selectedItem = scene.SelectedItem;
|
||||
return selectedItem != null && selectedItem.VisibleMeshes().Count() > 1;
|
||||
},
|
||||
},
|
||||
new SceneSelectionOperation()
|
||||
{
|
||||
OperationType = typeof(SubtractAndReplaceObject3D_2),
|
||||
TitleResolver = () => "Subtract & Replace".Localize(),
|
||||
Action = (sceneContext) => new SubtractAndReplaceObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene),
|
||||
Icon = (invertIcon) => AggContext.StaticData.LoadIcon("subtract_and_replace.png").SetPreMultiply(),
|
||||
IsEnabled = (scene) =>
|
||||
{
|
||||
var selectedItem = scene.SelectedItem;
|
||||
return selectedItem != null && selectedItem.VisibleMeshes().Count() > 1;
|
||||
},
|
||||
new SceneSelectionOperation()
|
||||
{
|
||||
OperationType = typeof(CombineObject3D_2),
|
||||
TitleResolver = () => "Combine".Localize(),
|
||||
Action = (sceneContext) => new CombineObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene),
|
||||
Icon = (invertIcon) => AggContext.StaticData.LoadIcon("combine.png").SetPreMultiply(),
|
||||
IsEnabled = (scene) =>
|
||||
{
|
||||
var selectedItem = scene.SelectedItem;
|
||||
return selectedItem != null && selectedItem.VisibleMeshes().Count() > 1;
|
||||
},
|
||||
},
|
||||
new SceneSelectionOperation()
|
||||
{
|
||||
OperationType = typeof(SubtractObject3D_2),
|
||||
TitleResolver = () => "Subtract".Localize(),
|
||||
Action = (sceneContext) => new SubtractObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene),
|
||||
Icon = (invertIcon) => AggContext.StaticData.LoadIcon("subtract.png").SetPreMultiply(),
|
||||
IsEnabled = (scene) =>
|
||||
{
|
||||
var selectedItem = scene.SelectedItem;
|
||||
return selectedItem != null && scene.SelectedItem.VisibleMeshes().Count() > 1;
|
||||
},
|
||||
},
|
||||
new SceneSelectionOperation()
|
||||
{
|
||||
OperationType = typeof(IntersectionObject3D_2),
|
||||
TitleResolver = () => "Intersect".Localize(),
|
||||
Action = (sceneContext) => new IntersectionObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene),
|
||||
Icon = (invertIcon) => AggContext.StaticData.LoadIcon("intersect.png"),
|
||||
IsEnabled = (scene) =>
|
||||
{
|
||||
var selectedItem = scene.SelectedItem;
|
||||
return selectedItem != null && selectedItem.VisibleMeshes().Count() > 1;
|
||||
},
|
||||
},
|
||||
new SceneSelectionOperation()
|
||||
{
|
||||
OperationType = typeof(SubtractAndReplaceObject3D_2),
|
||||
TitleResolver = () => "Subtract & Replace".Localize(),
|
||||
Action = (sceneContext) => new SubtractAndReplaceObject3D_2().WrapSelectedItemAndSelect(sceneContext.Scene),
|
||||
Icon = (invertIcon) => AggContext.StaticData.LoadIcon("subtract_and_replace.png").SetPreMultiply(),
|
||||
IsEnabled = (scene) =>
|
||||
{
|
||||
var selectedItem = scene.SelectedItem;
|
||||
return selectedItem != null && selectedItem.VisibleMeshes().Count() > 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
new SceneSelectionSeparator(),
|
||||
new SceneSelectionOperation()
|
||||
|
|
@ -1176,6 +1184,7 @@ namespace MatterHackers.MatterControl
|
|||
}
|
||||
|
||||
static int applicationInstanceCount = 0;
|
||||
|
||||
public static int ApplicationInstanceCount
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -410,6 +410,45 @@ namespace MatterHackers.MatterControl
|
|||
return popupMenu;
|
||||
}
|
||||
|
||||
public PopupMenuButton CreateSplitButton(SplitButtonParams buttonParams)
|
||||
{
|
||||
PopupMenuButton menuButton = null;
|
||||
|
||||
var innerButton = new IconButton(buttonParams.Icon, this)
|
||||
{
|
||||
Name = buttonParams.ButtonName + " Inner SplitButton",
|
||||
ToolTipText = buttonParams.DefaultActionTooltip,
|
||||
};
|
||||
|
||||
innerButton.Click += (s, e) =>
|
||||
{
|
||||
buttonParams.DefaultAction.Invoke(menuButton);
|
||||
};
|
||||
|
||||
// Remove right Padding for drop style
|
||||
innerButton.Padding = innerButton.Padding.Clone(right: 0);
|
||||
|
||||
menuButton = new PopupMenuButton(innerButton, this)
|
||||
{
|
||||
DynamicPopupContent = () =>
|
||||
{
|
||||
var popupMenu = new PopupMenu(ApplicationController.Instance.MenuTheme);
|
||||
buttonParams.ExtendPopupMenu?.Invoke(popupMenu);
|
||||
|
||||
return popupMenu;
|
||||
},
|
||||
Name = buttonParams.ButtonName + " Menu SplitButton",
|
||||
BackgroundColor = this.ToolbarButtonBackground,
|
||||
HoverColor = this.ToolbarButtonHover,
|
||||
MouseDownColor = this.ToolbarButtonDown,
|
||||
DrawArrow = true,
|
||||
Margin = this.ButtonSpacing,
|
||||
};
|
||||
|
||||
innerButton.Selectable = true;
|
||||
return menuButton;
|
||||
}
|
||||
|
||||
private static ImageBuffer ColorCircle(int size, Color color)
|
||||
{
|
||||
ImageBuffer imageBuffer = new ImageBuffer(size, size);
|
||||
|
|
@ -546,15 +585,35 @@ namespace MatterHackers.MatterControl
|
|||
public class PresetColors
|
||||
{
|
||||
public Color MaterialPreset { get; set; } = Color.Orange;
|
||||
|
||||
public Color QualityPreset { get; set; } = Color.Yellow;
|
||||
|
||||
public Color UserOverride { get; set; } = new Color(68, 95, 220, 150);
|
||||
}
|
||||
|
||||
public class GridColors
|
||||
{
|
||||
public Color Red { get; set; }
|
||||
|
||||
public Color Green { get; set; }
|
||||
|
||||
public Color Blue { get; set; }
|
||||
|
||||
public Color Line { get; set; }
|
||||
}
|
||||
|
||||
public class SplitButtonParams
|
||||
{
|
||||
public ImageBuffer Icon { get; set; }
|
||||
|
||||
public Action<GuiWidget> DefaultAction { get; set; }
|
||||
|
||||
public string DefaultActionTooltip { get; set; }
|
||||
|
||||
public Action MenuAction { get; set; }
|
||||
|
||||
public Action<PopupMenu> ExtendPopupMenu { get; set; }
|
||||
|
||||
public string ButtonName { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -49,9 +49,23 @@ namespace MatterHackers.Agg.UI
|
|||
public Func<string> TitleResolver { get; set; }
|
||||
|
||||
public string Title => this.TitleResolver?.Invoke();
|
||||
|
||||
public Func<string> HelpTextResolver { get; set; }
|
||||
|
||||
public string HelpText => this.HelpTextResolver?.Invoke();
|
||||
}
|
||||
|
||||
public class SceneSelectionSeparator : SceneSelectionOperation
|
||||
{
|
||||
}
|
||||
|
||||
public class OperationGroup : SceneSelectionOperation
|
||||
{
|
||||
public List<SceneSelectionOperation> Operations { get; set; } = new List<SceneSelectionOperation>();
|
||||
|
||||
public bool StickySelection { get; internal set; }
|
||||
|
||||
public string GroupName { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -252,14 +252,11 @@ namespace MatterHackers.MatterControl.CustomWidgets
|
|||
|
||||
public ImageBuffer IconImage => this.Enabled ? image : this.DisabledImage;
|
||||
|
||||
/// <summary>
|
||||
/// Switch icons without computing disabled image - use case for non-disableable toggle widgets
|
||||
/// </summary>
|
||||
/// <param name="icon"></param>
|
||||
internal void SetIcon(ImageBuffer icon)
|
||||
{
|
||||
image = icon;
|
||||
imageWidget.Image = icon;
|
||||
_disabledImage = null;
|
||||
}
|
||||
|
||||
private ImageBuffer _disabledImage;
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
public class ViewModeChangedEventArgs : EventArgs
|
||||
{
|
||||
public PartViewMode ViewMode { get; set; }
|
||||
|
||||
public PartViewMode PreviousMode { get; set; }
|
||||
}
|
||||
|
||||
|
|
@ -454,7 +455,67 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
|
||||
GuiWidget button;
|
||||
|
||||
if (namedAction.Icon != null)
|
||||
if (namedAction is OperationGroup operationGroup)
|
||||
{
|
||||
SceneSelectionOperation defaultOperation;
|
||||
|
||||
string groupRecordID = $"ActiveButton_{operationGroup.GroupName}_Group";
|
||||
|
||||
if (operationGroup.StickySelection)
|
||||
{
|
||||
int.TryParse(UserSettings.Instance.get(groupRecordID), out int activeButtonID);
|
||||
|
||||
activeButtonID = agg_basics.Clamp(activeButtonID, 0, operationGroup.Operations.Count - 1);
|
||||
|
||||
defaultOperation = operationGroup.Operations[activeButtonID];
|
||||
}
|
||||
else
|
||||
{
|
||||
defaultOperation = operationGroup.Operations.First();
|
||||
}
|
||||
|
||||
PopupMenuButton groupButton = null;
|
||||
|
||||
groupButton = theme.CreateSplitButton(new SplitButtonParams()
|
||||
{
|
||||
Icon = defaultOperation.Icon(theme.InvertIcons),
|
||||
DefaultAction = (menuButton) =>
|
||||
{
|
||||
defaultOperation.Action.Invoke(sceneContext);
|
||||
},
|
||||
DefaultActionTooltip = defaultOperation.HelpText ?? defaultOperation.Title,
|
||||
ButtonName = defaultOperation.Title,
|
||||
ExtendPopupMenu = (PopupMenu popupMenu) =>
|
||||
{
|
||||
foreach (var operation in operationGroup.Operations)
|
||||
{
|
||||
var operationMenu = popupMenu.CreateMenuItem(operation.Title, operation.Icon?.Invoke(theme.InvertIcons));
|
||||
operationMenu.Click += (s, e) => UiThread.RunOnIdle(() =>
|
||||
{
|
||||
if (operationGroup.StickySelection
|
||||
&& defaultOperation != operation)
|
||||
{
|
||||
// Update button
|
||||
var iconButton = groupButton.Children.OfType<IconButton>().First();
|
||||
iconButton.SetIcon(operation.Icon(theme.InvertIcons));
|
||||
iconButton.ToolTipText = operation.HelpText ?? operation.Title;
|
||||
|
||||
UserSettings.Instance.set(groupRecordID, operationGroup.Operations.IndexOf(operation).ToString());
|
||||
|
||||
defaultOperation = operation;
|
||||
|
||||
iconButton.Invalidate();
|
||||
}
|
||||
|
||||
operation.Action?.Invoke(sceneContext);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
button = groupButton;
|
||||
}
|
||||
else if (namedAction.Icon != null)
|
||||
{
|
||||
// add the create support before the align
|
||||
if (namedAction.OperationType == typeof(AlignObject3D))
|
||||
|
|
@ -487,16 +548,18 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
|
||||
operationButtons.Add((button, namedAction));
|
||||
|
||||
button.Click += (s, e) =>
|
||||
// Only bind Click event if not a SplitButton
|
||||
if (!(button is PopupMenuButton))
|
||||
{
|
||||
UiThread.RunOnIdle(() =>
|
||||
button.Click += (s, e) => UiThread.RunOnIdle(() =>
|
||||
{
|
||||
namedAction.Action.Invoke(sceneContext);
|
||||
var partTab = button.Parents<PartTabPage>().FirstOrDefault();
|
||||
var view3D = partTab.Descendants<View3DWidget>().FirstOrDefault();
|
||||
view3D.InteractionLayer.Focus();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
this.AddChild(button);
|
||||
}
|
||||
|
||||
|
|
@ -776,83 +839,56 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
|
|||
|
||||
private GuiWidget CreateSaveButton(ThemeConfig theme)
|
||||
{
|
||||
PopupMenuButton saveButton = null;
|
||||
|
||||
var iconButton = new IconButton(
|
||||
AggContext.StaticData.LoadIcon("save_grey_16x.png", 16, 16, theme.InvertIcons),
|
||||
theme)
|
||||
return theme.CreateSplitButton(new SplitButtonParams()
|
||||
{
|
||||
ToolTipText = "Save".Localize(),
|
||||
};
|
||||
|
||||
iconButton.Click += (s, e) =>
|
||||
{
|
||||
ApplicationController.Instance.Tasks.Execute("Saving".Localize(), sceneContext.Printer, async(progress, cancellationToken) =>
|
||||
ButtonName = "Save",
|
||||
Icon = AggContext.StaticData.LoadIcon("save_grey_16x.png", 16, 16, theme.InvertIcons),
|
||||
DefaultAction = (menuButton) =>
|
||||
{
|
||||
saveButton.Enabled = false;
|
||||
|
||||
try
|
||||
ApplicationController.Instance.Tasks.Execute("Saving".Localize(), sceneContext.Printer, async (progress, cancellationToken) =>
|
||||
{
|
||||
await sceneContext.SaveChanges(progress, cancellationToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
menuButton.Enabled = false;
|
||||
|
||||
saveButton.Enabled = true;
|
||||
}).ConfigureAwait(false);
|
||||
};
|
||||
try
|
||||
{
|
||||
await sceneContext.SaveChanges(progress, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ApplicationController.Instance.LogError("Error saving file".Localize() + ": " + ex.Message);
|
||||
}
|
||||
|
||||
// Remove right Padding for drop style
|
||||
iconButton.Padding = iconButton.Padding.Clone(right: 0);
|
||||
|
||||
saveButton = new PopupMenuButton(iconButton, theme)
|
||||
{
|
||||
Name = "Save SplitButton",
|
||||
ToolTipText = "Save As".Localize(),
|
||||
DynamicPopupContent = () =>
|
||||
menuButton.Enabled = true;
|
||||
}).ConfigureAwait(false);
|
||||
},
|
||||
DefaultActionTooltip = "Save".Localize(),
|
||||
ExtendPopupMenu = (PopupMenu popupMenu) =>
|
||||
{
|
||||
var popupMenu = new PopupMenu(ApplicationController.Instance.MenuTheme);
|
||||
|
||||
var saveAs = popupMenu.CreateMenuItem("Save As".Localize());
|
||||
saveAs.Click += (s, e) => UiThread.RunOnIdle(() =>
|
||||
{
|
||||
UiThread.RunOnIdle(() =>
|
||||
{
|
||||
DialogWindow.Show(
|
||||
new SaveAsPage(
|
||||
async (newName, destinationContainer) =>
|
||||
DialogWindow.Show(
|
||||
new SaveAsPage(
|
||||
(newName, destinationContainer) =>
|
||||
{
|
||||
// Save to the destination provider
|
||||
if (destinationContainer is ILibraryWritableContainer writableContainer)
|
||||
{
|
||||
// Save to the destination provider
|
||||
if (destinationContainer is ILibraryWritableContainer writableContainer)
|
||||
// Wrap stream with ReadOnlyStream library item and add to container
|
||||
writableContainer.Add(new[]
|
||||
{
|
||||
// Wrap stream with ReadOnlyStream library item and add to container
|
||||
writableContainer.Add(new[]
|
||||
{
|
||||
new InMemoryLibraryItem(sceneContext.Scene)
|
||||
{
|
||||
Name = newName
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
destinationContainer.Dispose();
|
||||
}
|
||||
}));
|
||||
});
|
||||
destinationContainer.Dispose();
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
return popupMenu;
|
||||
},
|
||||
BackgroundColor = theme.ToolbarButtonBackground,
|
||||
HoverColor = theme.ToolbarButtonHover,
|
||||
MouseDownColor = theme.ToolbarButtonDown,
|
||||
DrawArrow = true,
|
||||
Margin = theme.ButtonSpacing,
|
||||
};
|
||||
|
||||
iconButton.Selectable = true;
|
||||
|
||||
return saveButton;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnClosed(EventArgs e)
|
||||
|
|
|
|||
|
|
@ -686,7 +686,7 @@ namespace MatterHackers.MatterControl.Tests.Automation
|
|||
|
||||
public static void SaveBedplateToFolder(this AutomationRunner testRunner, string newFileName, string folderName)
|
||||
{
|
||||
testRunner.ClickByName("Save SplitButton", offset: new Point2D(8, 0));
|
||||
testRunner.ClickByName("Save Menu SplitButton", offset: new Point2D(8, 0));
|
||||
|
||||
testRunner.ClickByName("Save As Menu Item");
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue