indicatrix-cut 0.7.2

Desktop faceting-design editor: library browsing, spectral 3D rendering, material retargeting, and a solid inspection view.
import { Theme } from "../theme.slint";
import { EditorModel } from "../models/editor.slint";
import { GuideModel } from "../models/guide.slint";

// "Export Cutting Sheet" and "Export Diagram" live on one "Export..." split
// button on the command bar, next to Save, instead of as separate
// File menu entries. Mounted with one line in `editor_command_bar.slint`.
// "Export as Gem Cut Studio (.gcs)..." (`EditorModel.export_gcs()`) is here too,
// with its "experimental" tooltip, and also on the File menu; it is an advanced
// export, so the Simple interface leaves it out of both.
//
// Keeps the existing callback names -- `EditorModel.export_cutting_sheet()`/
// `export_diagram()`, unchanged from the File menu's own `activated` handlers.
//
// Keyboard and screen reader: Tab reaches the button, Space, Enter or Down opens the menu and
// puts the focus on its first item, Tab walks the items, Space or Enter runs one, Escape closes
// the menu and brings the focus back to the button.
export component ExportSplitButton inherits Rectangle {
    // `false` while the worked-example guide locks file actions (see `GuideAllow`).
    in property <bool> enabled: true;
    property <bool> menu_open: false;

    width: btn_row.preferred-width;
    height: 26px;

    // Closes the menu and returns the focus to the button.
    function close_menu() {
        root.menu_open = false;
        main_focus.focus();
    }
    function run_cutting_sheet() {
        root.close_menu();
        EditorModel.export_cutting_sheet();
    }
    function run_diagram() {
        root.close_menu();
        EditorModel.export_diagram();
    }
    function run_gcs() {
        root.close_menu();
        EditorModel.export_gcs();
    }

    btn_row := HorizontalLayout {
        Rectangle {
            width: main_txt.preferred-width + 16px;
            background: main_touch.has-hover ? Theme.bg-elevated : Theme.bg-input;
            border-radius: Theme.radius-sm;
            // The export tutorials outline this button (a cyan, 2px border).
            border-width: main_focus.has-focus || GuideModel.highlight_target == "export_menu" ? 2px : 0px;
            border-color: GuideModel.highlight_target == "export_menu" ? Theme.accent-cyan : Theme.accent-cyan-hover;
            opacity: root.enabled ? 1.0 : 0.4;

            accessible-role: AccessibleRole.button;
            accessible-label: "Export";
            accessible-description: GuideModel.shows_advanced()
                ? "Export the cutting sheet as a page, the diagram as a picture, or the design as a Gem Cut Studio file."
                : "Export the cutting sheet as a page, or the diagram as a picture.";
            accessible-expandable: true;
            accessible-expanded: root.menu_open;
            accessible-enabled: root.enabled;
            accessible-action-default => {
                if root.enabled {
                    root.menu_open = !root.menu_open;
                }
            }

            main_focus := FocusScope {
                enabled: root.enabled;
                key-pressed(event) => {
                    if event.text == Key.Space || event.text == Key.Return || event.text == Key.DownArrow {
                        root.menu_open = true;
                        return accept;
                    } else if event.text == Key.Escape && root.menu_open {
                        root.menu_open = false;
                        return accept;
                    }
                    return reject;
                }
            }
            main_txt := Text {
                text: "Export...";
                font-size: 11px;
                font-weight: 600;
                color: Theme.text-primary;
                horizontal-alignment: center;
                vertical-alignment: center;
            }
            main_touch := TouchArea {
                enabled: root.enabled;
                mouse-cursor: root.enabled ? MouseCursor.pointer : MouseCursor.default;
                clicked => { root.menu_open = !root.menu_open; }
            }
            Tooltip { text: root.enabled
                ? (GuideModel.shows_advanced()
                    ? @markdown("Export the cutting sheet (a printable page), the diagram (a picture), or the design as a Gem Cut Studio (.gcs) file.")
                    : @markdown("Export the cutting sheet (a printable page) or the diagram (a picture)."))
                : @markdown("\{GuideModel.locked_hint}"); }
        }
    }

    if root.menu_open && root.enabled: Rectangle {
        // Backdrop -- dismisses the menu on an outside click, matching
        // `header.slint`'s own floating-panel convention (`filter_popup`/
        // `import_popup`), the closest existing analogue in this app to a
        // click-to-open dropdown that is not a native `ContextMenuArea`.
        x: -2000px;
        y: -2000px;
        width: 4000px;
        height: 4000px;
        z: 49;
        TouchArea { clicked => { root.menu_open = false; } }
    }

    if root.menu_open && root.enabled: Rectangle {
        x: 0px;
        y: root.height + 2px;
        z: 50;
        width: 220px;
        height: menu_col.preferred-height;
        background: Theme.surface-dialog;
        border-radius: Theme.radius-sm;
        border-width: 1px;
        border-color: Theme.border-dialog;
        drop-shadow-blur: 12px;
        drop-shadow-color: Theme.modal-shadow;
        clip: true;
        accessible-role: AccessibleRole.list;
        accessible-label: "Export formats";

        // Opening the menu from the keyboard leaves the focus in it.
        init => { item1_focus.focus(); }

        menu_col := VerticalLayout {
            Rectangle {
                height: 26px;
                background: (item1_touch.has-hover || item1_focus.has-focus) ? Theme.bg-card-hover : transparent;
                border-width: item1_focus.has-focus ? 2px : 0px;
                border-color: Theme.accent-cyan-hover;
                accessible-role: AccessibleRole.button;
                accessible-label: "Export Cutting Sheet (HTML)";
                accessible-description: "A printable page with every facet in cutting order.";
                accessible-action-default => { root.run_cutting_sheet(); }
                item1_focus := FocusScope {
                    key-pressed(event) => {
                        if event.text == Key.Space || event.text == Key.Return {
                            root.run_cutting_sheet();
                            return accept;
                        } else if event.text == Key.Escape {
                            root.close_menu();
                            return accept;
                        }
                        return reject;
                    }
                }
                Text {
                    text: "Export Cutting Sheet (HTML)...";
                    font-size: 10px;
                    color: Theme.text-primary;
                    vertical-alignment: center;
                    horizontal-alignment: left;
                    x: 8px;
                }
                item1_touch := TouchArea {
                    clicked => { root.run_cutting_sheet(); }
                }
                Tooltip { text: @markdown("A printable page with every facet in cutting order."); }
            }
            Rectangle {
                height: 26px;
                background: (item2_touch.has-hover || item2_focus.has-focus) ? Theme.bg-card-hover : transparent;
                border-width: item2_focus.has-focus ? 2px : 0px;
                border-color: Theme.accent-cyan-hover;
                accessible-role: AccessibleRole.button;
                accessible-label: "Export Diagram (PNG)";
                accessible-description: "A picture of the design diagram.";
                accessible-action-default => { root.run_diagram(); }
                item2_focus := FocusScope {
                    key-pressed(event) => {
                        if event.text == Key.Space || event.text == Key.Return {
                            root.run_diagram();
                            return accept;
                        } else if event.text == Key.Escape {
                            root.close_menu();
                            return accept;
                        }
                        return reject;
                    }
                }
                Text {
                    text: "Export Diagram (PNG)...";
                    font-size: 10px;
                    color: Theme.text-primary;
                    vertical-alignment: center;
                    horizontal-alignment: left;
                    x: 8px;
                }
                item2_touch := TouchArea {
                    clicked => { root.run_diagram(); }
                }
                Tooltip { text: @markdown("A picture (PNG) of the design diagram."); }
            }
            // Advanced: the Simple interface leaves the Gem Cut Studio export out.
            if GuideModel.shows_advanced(): Rectangle {
                height: 26px;
                background: (item3_touch.has-hover || item3_focus.has-focus) ? Theme.bg-card-hover : transparent;
                border-width: item3_focus.has-focus ? 2px : 0px;
                border-color: Theme.accent-cyan-hover;
                accessible-role: AccessibleRole.button;
                accessible-label: "Export as Gem Cut Studio (.gcs)";
                accessible-description: "Experimental. Written from the published Gem Cut Studio 1.1 file description; chiral designs are untested.";
                accessible-action-default => { root.run_gcs(); }
                item3_focus := FocusScope {
                    key-pressed(event) => {
                        if event.text == Key.Space || event.text == Key.Return {
                            root.run_gcs();
                            return accept;
                        } else if event.text == Key.Escape {
                            root.close_menu();
                            return accept;
                        }
                        return reject;
                    }
                }
                Text {
                    text: "Export as Gem Cut Studio (.gcs)...";
                    font-size: 10px;
                    color: Theme.text-primary;
                    vertical-alignment: center;
                    horizontal-alignment: left;
                    x: 8px;
                }
                item3_touch := TouchArea {
                    clicked => { root.run_gcs(); }
                }
                Tooltip { text: @markdown("Experimental: written from the published Gem Cut Studio 1.1 file description; chiral designs are untested."); }
            }
        }
    }
}