qleany 1.7.2

Architecture generator for Rust and C++/Qt applications.
import { ScrollView } from "std-widgets.slint";
import { CheckState, AppState } from "../globals.slint";
import { StyledButton } from "misc.slint";

// Floating action button for check status
component CheckFab inherits Rectangle {
    callback clicked();

    width: 48px;
    height: 48px;
    border-radius: 24px;
    background: CheckState.check-status == "critical" ? #fa5252 :
                CheckState.check-status == "warning" ? #fd7e14 : #40c057;
    drop-shadow-blur: 8px;
    drop-shadow-color: #00000030;
    drop-shadow-offset-y: 2px;

    animate background { duration: 200ms; }

    touch := TouchArea {
        mouse-cursor: pointer;
        clicked => { root.clicked(); }
    }

    VerticalLayout {
        alignment: center;
        HorizontalLayout {
            alignment: center;

            if CheckState.check-status == "critical" : Text {
                text: CheckState.critical-count > 9 ? "9+" : CheckState.critical-count;
                font-size: 18px;
                font-weight: 700;
                color: #ffffff;
                horizontal-alignment: center;
                vertical-alignment: center;
            }
            if CheckState.check-status == "warning" : Text {
                text: CheckState.warning-count > 9 ? "9+" : CheckState.warning-count;
                font-size: 18px;
                font-weight: 700;
                color: #ffffff;
                horizontal-alignment: center;
                vertical-alignment: center;
            }
            if CheckState.check-status == "ok" : Text {
                text: "\u{2713}";
                font-size: 22px;
                font-weight: 700;
                color: #ffffff;
                horizontal-alignment: center;
                vertical-alignment: center;
            }
        }
    }
}

// Issue row in the side panel
component IssueRow inherits Rectangle {
    in property <string> message;
    in property <bool> is-critical: false;

    min-height: 40px;
    background: is-critical ? #fff5f5 : #fff9db;
    border-radius: 4px;
    border-width: 1px;
    border-color: is-critical ? #ffc9c9 : #ffec99;

    HorizontalLayout {
        padding: 8px;
        spacing: 8px;

        Rectangle {
            width: 4px;
            background: is-critical ? #fa5252 : #fd7e14;
            border-radius: 2px;
        }

        Text {
            text: message;
            font-size: 13px;
            color: #495057;
            wrap: word-wrap;
            vertical-alignment: center;
        }
    }
}

// Side panel showing check results
component CheckPanel inherits Rectangle {
    width: 360px;
    background: #ffffff;
    border-width: 1px;
    border-color: #dee2e6;
    border-radius: 8px;
    drop-shadow-blur: 8px;
    drop-shadow-color: #00000020;
    drop-shadow-offset-x: -2px;

    VerticalLayout {
        padding: 16px;
        spacing: 12px;

        // Header
        HorizontalLayout {
            spacing: 8px;

            Text {
                text: "Manifest Check";
                font-size: 16px;
                font-weight: 600;
                color: #212529;
                vertical-alignment: center;
            }

            Rectangle { horizontal-stretch: 1; }

            // Re-check button
            StyledButton {
                text: "Re-check";
                min-width: 80px;
                clicked => { CheckState.request-check(); }
            }

            // Close button
            Rectangle {
                width: 28px;
                height: 28px;
                border-radius: 14px;
                background: close-touch.has-hover ? #f1f3f5 : transparent;

                close-touch := TouchArea {
                    mouse-cursor: pointer;
                    clicked => { CheckState.panel-visible = false; }
                }

                Text {
                    text: "\u{2715}";
                    font-size: 16px;
                    color: #868e96;
                    horizontal-alignment: center;
                    vertical-alignment: center;
                }
            }
        }

        // Summary
        HorizontalLayout {
            spacing: 16px;

            if CheckState.critical-count > 0 : HorizontalLayout {
                spacing: 4px;
                Rectangle {
                    width: 10px;
                    height: 10px;
                    border-radius: 5px;
                    background: #fa5252;
                    y: (parent.height - self.height) / 2;
                }
                Text {
                    text: CheckState.critical-count + " critical";
                    font-size: 13px;
                    font-weight: 500;
                    color: #fa5252;
                    vertical-alignment: center;
                }
            }
            if CheckState.warning-count > 0 : HorizontalLayout {
                spacing: 4px;
                Rectangle {
                    width: 10px;
                    height: 10px;
                    border-radius: 5px;
                    background: #fd7e14;
                    y: (parent.height - self.height) / 2;
                }
                Text {
                    text: CheckState.warning-count + " warning" + (CheckState.warning-count > 1 ? "s" : "");
                    font-size: 13px;
                    font-weight: 500;
                    color: #fd7e14;
                    vertical-alignment: center;
                }
            }
            if CheckState.critical-count == 0 && CheckState.warning-count == 0 : Text {
                text: "No issues found";
                font-size: 13px;
                font-weight: 500;
                color: #40c057;
                vertical-alignment: center;
            }
        }

        // Divider
        Rectangle {
            height: 1px;
            background: #dee2e6;
        }

        // Scrollable issue list
        ScrollView {
            vertical-stretch: 1;

            VerticalLayout {
                spacing: 6px;
                alignment: start;

                // Critical errors first
                for error in CheckState.critical-errors : IssueRow {
                    message: error;
                    is-critical: true;
                }

                // Then warnings
                for warning in CheckState.warnings : IssueRow {
                    message: warning;
                    is-critical: false;
                }
            }
        }
    }
}

// Main exported widget combining FAB + Panel
export component CheckWidget inherits Rectangle {
    // Overlay to close panel when clicking outside
    if CheckState.panel-visible : Rectangle {
        x: 0px;
        y: 0px;
        width: root.width;
        height: root.height;

        TouchArea {
            clicked => { CheckState.panel-visible = false; }
        }
    }

    // Side panel (anchored to right edge)
    if CheckState.panel-visible : CheckPanel {
        x: root.width - self.width;
        y: 0px;
        height: root.height;
    }

    // Floating action button (bottom-right, above panel if open)
    if AppState.manifest-is-open && CheckState.check-status != "none" : CheckFab {
        x: CheckState.panel-visible ? root.width - 360px - self.width - 16px : root.width - self.width - 24px;
        y: root.height - self.height - 24px;

        animate x { duration: 200ms; easing: ease-out; }

        clicked => {
            CheckState.panel-visible = !CheckState.panel-visible;
        }
    }
}