public PrintPopupMenu(PrinterConfig printer, ThemeConfig theme)
            : base(theme)
        {
            this.printer         = printer;
            this.DrawArrow       = true;
            this.BackgroundColor = theme.ToolbarButtonBackground;
            this.HoverColor      = theme.ToolbarButtonHover;
            this.MouseDownColor  = theme.ToolbarButtonDown;
            this.Name            = "PrintPopupMenu";
            this.HAnchor         = HAnchor.Fit;
            this.VAnchor         = VAnchor.Fit;

            settingsContext = new SettingsContext(printer, null, NamedSettingsLayers.All);

            this.PopupHAnchor   = HAnchor.Fit;
            this.PopupVAnchor   = VAnchor.Fit;
            this.MakeScrollable = false;

            this.DynamicPopupContent = () =>
            {
                var menuTheme = ApplicationController.Instance.MenuTheme;

                int tabIndex = 0;

                allUiFields.Clear();

                var printPanel = new FlowLayoutWidget(FlowDirection.TopToBottom)
                {
                    Padding         = theme.DefaultContainerPadding,
                    BackgroundColor = menuTheme.BackgroundColor
                };

                printPanel.AddChild(new TextWidget("Options".Localize(), textColor: menuTheme.TextColor, pointSize: theme.DefaultFontSize)
                {
                    HAnchor = HAnchor.Left
                });

                var optionsPanel = new IgnoredFlowLayout()
                {
                    Name        = "PrintPopupMenu Panel",
                    HAnchor     = HAnchor.Fit | HAnchor.Left,
                    VAnchor     = VAnchor.Fit,
                    Padding     = 5,
                    MinimumSize = new Vector2(400, 65),
                };
                printPanel.AddChild(optionsPanel);

                foreach (var key in new[] { SettingsKey.layer_height, SettingsKey.fill_density, SettingsKey.create_raft })
                {
                    var settingsData = PrinterSettings.SettingsData[key];
                    var row          = SliceSettingsTabView.CreateItemRow(settingsData, settingsContext, printer, menuTheme, ref tabIndex, allUiFields);

                    if (row is SliceSettingsRow settingsRow)
                    {
                        settingsRow.ArrowDirection = ArrowDirection.Left;
                    }

                    optionsPanel.AddChild(row);
                }

                var subPanel = new FlowLayoutWidget(FlowDirection.TopToBottom)
                {
                    Margin = new BorderDouble(2, 0)
                };

                bool anySettingOverridden = false;
                anySettingOverridden |= printer.Settings.GetValue <bool>(SettingsKey.spiral_vase);
                anySettingOverridden |= !string.IsNullOrWhiteSpace(printer.Settings.GetValue(SettingsKey.layer_to_pause));

                var sectionWidget = new SectionWidget("Advanced".Localize(), subPanel, menuTheme, expanded: anySettingOverridden)
                {
                    Name    = "Advanced Section",
                    HAnchor = HAnchor.Stretch,
                    VAnchor = VAnchor.Fit,
                    Margin  = 0
                };
                printPanel.AddChild(sectionWidget);

                foreach (var key in new[] { SettingsKey.spiral_vase, SettingsKey.layer_to_pause })
                {
                    var advancedRow = SliceSettingsTabView.CreateItemRow(
                        PrinterSettings.SettingsData[key],
                        settingsContext,
                        printer,
                        menuTheme,
                        ref tabIndex,
                        allUiFields);

                    if (advancedRow is SliceSettingsRow settingsRow)
                    {
                        settingsRow.ArrowDirection = ArrowDirection.Left;
                    }

                    subPanel.AddChild(advancedRow);
                }

                menuTheme.ApplyBoxStyle(sectionWidget);

                sectionWidget.Margin = new BorderDouble(0, 10);
                sectionWidget.ContentPanel.Children <SettingsRow>().First().Border = new BorderDouble(0, 1);
                sectionWidget.ContentPanel.Children <SettingsRow>().Last().Border  = 0;

                var printerReadyToTakeCommands = printer.Connection.CommunicationState == PrinterCommunication.CommunicationStates.FinishedPrint ||
                                                 printer.Connection.CommunicationState == PrinterCommunication.CommunicationStates.Connected;

                // add the start print button
                var setupRow = new FlowLayoutWidget()
                {
                    HAnchor = HAnchor.Stretch
                };

                // Perform validation before popup
                var errors = printer.Validate();

                // Enable print option when no validation Errors exists
                var printEnabled = !errors.Any(err => err.ErrorLevel == ValidationErrorLevel.Error);

                var startPrintButton = new TextButton("Start Print".Localize(), menuTheme)
                {
                    Name    = "Start Print Button",
                    Enabled = printEnabled
                };

                startPrintButton.Click += (s, e) =>
                {
                    // Exit if the bed is not GCode and the bed has no printable items
                    if (!printer.Bed.EditContext.IsGGCodeSource &&
                        !printer.PrintableItems(printer.Bed.Scene).Any())
                    {
                        return;
                    }

                    UiThread.RunOnIdle(async() =>
                    {
                        // Save any pending changes before starting print operation
                        await ApplicationController.Instance.Tasks.Execute("Saving Changes".Localize(), printer, printer.Bed.SaveChanges);

                        await ApplicationController.Instance.PrintPart(
                            printer.Bed.EditContext,
                            printer,
                            null,
                            CancellationToken.None);
                    });

                    this.CloseMenu();
                };

                var hasErrors   = errors.Any(e => e.ErrorLevel == ValidationErrorLevel.Error);
                var hasWarnings = errors.Any(e => e.ErrorLevel == ValidationErrorLevel.Warning &&
                                             UserSettings.Instance.get($"Ignore_{e.ID}") != "true");

                var hasErrorsOrWarnings = hasErrors || hasWarnings;
                if (hasErrorsOrWarnings)
                {
                    string label = hasErrors ? "Action Required".Localize() : "Action Recommended".Localize();

                    setupRow.AddChild(new TextWidget(label, textColor: hasErrors ? Color.Red : theme.PrimaryAccentColor, pointSize: theme.DefaultFontSize)
                    {
                        VAnchor = VAnchor.Bottom,
                        AutoExpandBoundsToText = true,
                    });
                }

                setupRow.AddChild(new HorizontalSpacer());

                // Export button {{
                bool isSailfish       = printer.Settings.GetValue <bool>("enable_sailfish_communication");
                var  exportPlugins    = PluginFinder.CreateInstancesOf <IExportPlugin>();
                var  targetPluginType = isSailfish ? typeof(X3GExport) : typeof(GCodeExport);

                // Find the first export plugin with the target type
                if (exportPlugins.FirstOrDefault(p => p.GetType() == targetPluginType) is IExportPlugin exportPlugin)
                {
                    string exportType = isSailfish ? "Export X3G".Localize() : "Export G-Code".Localize();

                    exportPlugin.Initialize(printer);

                    var exportGCodeButton = menuTheme.CreateDialogButton("Export".Localize());
                    exportGCodeButton.Name        = "Export Gcode Button";
                    exportGCodeButton.Enabled     = exportPlugin.Enabled;
                    exportGCodeButton.ToolTipText = exportPlugin.Enabled ? exportType : exportPlugin.DisabledReason;

                    exportGCodeButton.Click += (s, e) =>
                    {
                        ExportPrintItemPage.DoExport(
                            new[] { new InMemoryLibraryItem(printer.Bed.Scene) },
                            printer,
                            exportPlugin);
                    };

                    setupRow.AddChild(exportGCodeButton);
                }

                // Export button }}

                setupRow.AddChild(startPrintButton);

                printPanel.AddChild(setupRow);

                if (printEnabled)
                {
                    theme.ApplyPrimaryActionStyle(startPrintButton);
                }
                else
                {
                    startPrintButton.BackgroundColor = theme.MinimalShade;
                }

                if (hasErrorsOrWarnings)
                {
                    var errorsPanel = new ValidationErrorsPanel(errors, menuTheme);

                    // Conditional layout for right or bottom errors panel alignment
                    var layoutStyle = FlowDirection.TopToBottom;

                    if (layoutStyle == FlowDirection.LeftToRight)
                    {
                        errorsPanel.HAnchor         = HAnchor.Absolute;
                        errorsPanel.VAnchor         = VAnchor.Fit | VAnchor.Top;
                        errorsPanel.BackgroundColor = theme.ResolveColor(menuTheme.BackgroundColor, theme.PrimaryAccentColor.WithAlpha(30));
                        errorsPanel.Width           = 350;

                        errorsPanel.Load += (s, e) =>
                        {
                            errorsPanel.Parent.BackgroundColor = Color.Transparent;
                        };
                    }
                    else
                    {
                        errorsPanel.HAnchor = HAnchor.Stretch;
                        errorsPanel.VAnchor = VAnchor.Fit;
                        errorsPanel.Margin  = 3;
                    }

                    // Instead of the typical case where the print panel is returned, wrap and append validation errors panel
                    var errorsContainer = new FlowLayoutWidget(layoutStyle)
                    {
                        HAnchor         = HAnchor.Fit,
                        VAnchor         = VAnchor.Fit,
                        BackgroundColor = layoutStyle == FlowDirection.TopToBottom ? printPanel.BackgroundColor : Color.Transparent
                    };

                    // Clear bottom padding
                    printPanel.Padding = printPanel.Padding.Clone(bottom: 2);

                    errorsContainer.AddChild(printPanel);
                    errorsContainer.AddChild(errorsPanel);

                    return(errorsContainer);
                }

                return(printPanel);
            };

            this.AddChild(new TextButton("Print".Localize(), theme)
            {
                Selectable = false,
                Padding    = theme.TextButtonPadding.Clone(right: 5)
            });

            // Register listeners
            printer.Settings.SettingChanged += Printer_SettingChanged;
        }
        public ExportSlaPopupMenu(PrinterConfig printer, ThemeConfig theme)
            : base(theme)
        {
            this.printer         = printer;
            this.DrawArrow       = true;
            this.BackgroundColor = theme.ToolbarButtonBackground;
            this.HoverColor      = theme.ToolbarButtonHover;
            this.MouseDownColor  = theme.ToolbarButtonDown;
            this.Name            = "ExportSlaPopupMenu";
            this.HAnchor         = HAnchor.Fit;
            this.VAnchor         = VAnchor.Fit;

            settingsContext = new SettingsContext(printer, null, NamedSettingsLayers.All);

            this.PopupHAnchor   = HAnchor.Fit;
            this.PopupVAnchor   = VAnchor.Fit;
            this.MakeScrollable = false;

            this.DynamicPopupContent = () =>
            {
                var menuTheme = ApplicationController.Instance.MenuTheme;

                int tabIndex = 0;

                allUiFields.Clear();

                var exportPanel = new FlowLayoutWidget(FlowDirection.TopToBottom)
                {
                    Padding         = theme.DefaultContainerPadding,
                    BackgroundColor = menuTheme.BackgroundColor
                };

                exportPanel.AddChild(new TextWidget("Options".Localize(), textColor: menuTheme.TextColor, pointSize: theme.DefaultFontSize)
                {
                    HAnchor = HAnchor.Left
                });

                var optionsPanel = new IgnoredFlowLayout()
                {
                    Name        = "ExportSlaPopupMenu Panel",
                    HAnchor     = HAnchor.Fit | HAnchor.Left,
                    VAnchor     = VAnchor.Fit,
                    Padding     = 5,
                    MinimumSize = new Vector2(400 * GuiWidget.DeviceScale, 65 * GuiWidget.DeviceScale),
                };
                exportPanel.AddChild(optionsPanel);

                var settingsToAdd = new[]
                {
                    SettingsKey.sla_layer_height,
                    SettingsKey.sla_create_raft,
                    SettingsKey.sla_auto_support
                };

                foreach (var key in settingsToAdd)
                {
                    var settingsData = PrinterSettings.SettingsData[key];
                    var row          = SliceSettingsTabView.CreateItemRow(settingsData, settingsContext, printer, menuTheme, ref tabIndex, allUiFields);

                    if (row is SliceSettingsRow settingsRow)
                    {
                        settingsRow.ArrowDirection = ArrowDirection.Left;
                    }

                    optionsPanel.AddChild(row);
                }

                // add the export print button
                var setupRow = new FlowLayoutWidget()
                {
                    HAnchor = HAnchor.Stretch
                };

                // Perform validation before popup
                var errors = printer.Validate();

                var hasErrors   = errors.Any(e => e.ErrorLevel == ValidationErrorLevel.Error);
                var hasWarnings = errors.Any(e => e.ErrorLevel == ValidationErrorLevel.Warning &&
                                             UserSettings.Instance.get($"Ignore_{e.ID}") != "true");

                var hasErrorsOrWarnings = hasErrors || hasWarnings;
                if (hasErrorsOrWarnings)
                {
                    string label = hasErrors ? "Action Required".Localize() : "Action Recommended".Localize();

                    setupRow.AddChild(new TextWidget(label, textColor: hasErrors ? Color.Red : theme.PrimaryAccentColor, pointSize: theme.DefaultFontSize)
                    {
                        VAnchor = VAnchor.Bottom,
                        AutoExpandBoundsToText = true,
                    });
                }

                setupRow.AddChild(new HorizontalSpacer());

                // Export button {{
                var exportPlugins = PluginFinder.CreateInstancesOf <IExportPlugin>();
                // set target as SLA export
                var targetPluginType = typeof(GCodeExport);

                // Find the first export plugin with the target type
                if (exportPlugins.FirstOrDefault(p => p.GetType() == targetPluginType) is IExportPlugin exportPlugin)
                {
                    string exportType = "Export Photon File".Localize();

                    exportPlugin.Initialize(printer);

                    var exportButton = menuTheme.CreateDialogButton("Export".Localize());
                    theme.ApplyPrimaryActionStyle(exportButton);

                    exportButton.Name        = "Export SLA Button";
                    exportButton.Enabled     = exportPlugin.Enabled;
                    exportButton.ToolTipText = exportPlugin.Enabled ? exportType : exportPlugin.DisabledReason;

                    exportButton.Click += (s, e) =>
                    {
                        this.CloseMenu();
                        ExportPrintItemPage.DoExport(
                            new[] { new InMemoryLibraryItem(printer.Bed.Scene) },
                            printer,
                            exportPlugin);
                    };

                    setupRow.AddChild(exportButton);
                }

                // Export button

                exportPanel.AddChild(setupRow);

                if (hasErrorsOrWarnings)
                {
                    var errorsPanel = new ValidationErrorsPanel(errors, menuTheme);

                    // Conditional layout for right or bottom errors panel alignment
                    var layoutStyle = FlowDirection.TopToBottom;

                    if (layoutStyle == FlowDirection.LeftToRight)
                    {
                        errorsPanel.HAnchor         = HAnchor.Absolute;
                        errorsPanel.VAnchor         = VAnchor.Fit | VAnchor.Top;
                        errorsPanel.BackgroundColor = theme.ResolveColor(menuTheme.BackgroundColor, theme.PrimaryAccentColor.WithAlpha(30));
                        errorsPanel.Width           = 350;

                        errorsPanel.Load += (s, e) =>
                        {
                            errorsPanel.Parent.BackgroundColor = Color.Transparent;
                        };
                    }
                    else
                    {
                        errorsPanel.HAnchor = HAnchor.Stretch;
                        errorsPanel.VAnchor = VAnchor.Fit;
                        errorsPanel.Margin  = 3;
                    }

                    // Instead of the typical case where the print panel is returned, wrap and append validation errors panel
                    var errorsContainer = new FlowLayoutWidget(layoutStyle)
                    {
                        HAnchor         = HAnchor.Fit,
                        VAnchor         = VAnchor.Fit,
                        BackgroundColor = layoutStyle == FlowDirection.TopToBottom ? exportPanel.BackgroundColor : Color.Transparent
                    };

                    // Clear bottom padding
                    exportPanel.Padding = exportPanel.Padding.Clone(bottom: 2);

                    errorsContainer.AddChild(exportPanel);
                    errorsContainer.AddChild(errorsPanel);

                    return(errorsContainer);
                }

                return(exportPanel);
            };

            this.AddChild(new TextButton("Export".Localize(), theme)
            {
                Selectable = false,
                Padding    = theme.TextButtonPadding.Clone(right: 5)
            });
        }