qleany 1.7.1

Architecture generator for Rust and C++/Qt applications.
import { LineEdit, ComboBox } from "std-widgets.slint";

// List item component
export component ListItem inherits Rectangle {
    in property <string> label;
    in property <bool> active: false;
    callback clicked();

    height: 40px;
    background: active ? #e7f5ff : transparent;
    border-radius: 4px;

    HorizontalLayout {
        padding-left: 12px;
        padding-right: 12px;
        alignment: start;

        Text {
            text: label;
            font-size: 14px;
            color: active ? #1971c2 : #495057;
            font-weight: active ? 500 : 400;
            vertical-alignment: center;
        }
    }

    TouchArea {
        clicked => { root.clicked(); }
    }
}

export component Breadcrumb inherits Rectangle {
    in property <[string]> path: [];

    height: 32px;
    background: #f8f9fa;
    border-radius: 4px;
    border-width: 1px;
    border-color: #dee2e6;

    HorizontalLayout {
        padding-left: 12px;
        padding-right: 12px;
        spacing: 8px;
        alignment: start;

        for item[i] in path : HorizontalLayout {
            spacing: 8px;
            
            Text {
                text: item;
                font-size: 13px;
                font-weight: (i == path.length - 1) ? 600 : 400;
                color: (i == path.length - 1) ? #212529 : #868e96;
                vertical-alignment: center;
            }

            if i < path.length - 1 : Text {
                text: ">";
                font-size: 13px;
                color: #dee2e6;
                vertical-alignment: center;
            }
        }
    }
}


// Vertical divider
export component Divider inherits Rectangle {
    width: 1px;
    background: #dee2e6;
}

// Section header
export component SectionHeader inherits Rectangle {
    in property <string> title;

    height: 48px;

    HorizontalLayout {
        padding: 12px;
        alignment: start;

        Text {
            text: title;
            font-size: 18px;
            font-weight: 600;
            color: #212529;
            vertical-alignment: center;
        }
    }
}

// Styled select/combo box
export component StyledComboBox inherits Rectangle {
    in-out property <string> current-value;
    in property <[string]> model;
    in property <bool> enabled: true;
    callback selected(string);

    height: 36px;
    width: root.preferred-width;
    background: enabled ? #ffffff : #f8f9fa;
    border-width: 1px;
    border-color: #ced4da;
    border-radius: 6px;

    HorizontalLayout {
        padding: 2px;
        alignment: start;

        combo := ComboBox {
            current-value <=> root.current-value;
            model: root.model;
            enabled: root.enabled;
            selected(val) => { root.selected(val); }
        }
    }
}

// Paper-like card component
export component Paper inherits Rectangle {
    in property <bool> with-border: true;
    in property <color> bg: #f8f9fa;

    background: bg;
    border-width: with-border ? 1px : 0px;
    border-color: #dee2e6;
    border-radius: 8px;
    drop-shadow-blur: 4px;
    drop-shadow-color: #00000010;
    drop-shadow-offset-y: 1px;
}



// Form field with label
export component FormField inherits VerticalLayout {
    in property <string> label;
    in property <bool> required: false;
    in property <string> error-message: "";

    spacing: 4px;

    HorizontalLayout {
        spacing: 4px;
        Text {
            text: label;
            font-size: 14px;
            font-weight: 500;
            color: error-message != "" ? #fa5252 : #212529;
        }
        if required : Text {
            text: "*";
            font-size: 14px;
            color: #fa5252;
        }
        // spacer
        Rectangle {
            horizontal-stretch: 1;
        }
    }

    @children

    if error-message != "" : Text {
        text: error-message;
        font-size: 12px;
        color: #fa5252;
    }

}


// Styled text input
export component StyledLineEdit inherits Rectangle {
    in-out property <string> text;
    in property <string> placeholder;
    in property <bool> enabled: true;
    in property <bool> has-error: false;
    callback edited(string);

    height: 36px;
    width: root.preferred-width;
    background: enabled ? #ffffff : #f8f9fa;
    border-width: 1px;
    border-color: has-error ? #fa5252 : (input.has-focus ? #228be6 : #ced4da);
    border-radius: 6px;

    HorizontalLayout {
        padding: 2px;
        alignment: start;

        input := LineEdit {
            text <=> root.text;
            placeholder-text: placeholder;
            enabled: root.enabled;
            edited(text) => {
                root.edited(text);
            }
        }
    }

}

// Alert component for messages (errors, info, etc.)
export component Alert inherits Rectangle {
    in property <string> message;
    in property <string> title: "Error";
    in property <color> alert-color: #fa5252;
    in property <color> bg-color: #fff5f5;
    
    background: bg-color;
    border-width: 1px;
    border-color: alert-color;
    border-radius: 8px;
    min-height: 48px;

    HorizontalLayout {
        padding: 12px;
        spacing: 12px;
        
        Rectangle {
            width: 4px;
            background: alert-color;
            border-radius: 2px;
        }
        
        VerticalLayout {
            spacing: 4px;
            alignment: center;
            
            Text {
                text: title;
                font-size: 14px;
                font-weight: 600;
                color: alert-color;
            }
            
            Text {
                text: message;
                font-size: 14px;
                color: #495057;
                wrap: word-wrap;
            }
        }
    }
}

// Custom StyledButton component
export component StyledButton inherits Rectangle {
    in property <string> text: "";
    in property <bool> primary: false;
    in property <bool> flat: false;
    in property <bool> outline: false;
    in property <color> bg-color: flat ? #ffffff : (primary ? #228be6 : (outline ? transparent : #e9ecef));
    in property <color> btn-border-color: flat ? #ffffff : (primary ? #228be6 : #ced4da);
    in property <color> text-color: primary ? #ffffff : (outline ? (bg-color == transparent ? #495057 : bg-color) : #495057);
    in property <bool> enabled: true;
    callback clicked();

    min-width: 80px;
    min-height: 36px;
    border-radius: 6px;
    background: outline ? transparent : (touch.pressed ? bg-color.darker(0.2) : (touch.has-hover ? bg-color.darker(0.1) : bg-color));
    border-width: 1px;
    border-color: touch.has-hover ? btn-border-color.darker(0.1) : btn-border-color;
    opacity: enabled ? 1.0 : 0.5;

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

    HorizontalLayout {
        padding: 8px;
        padding-left: 16px;
        padding-right: 16px;
        alignment: center;

        Text {
            text: text;
            font-size: 14px;
            font-weight: 500;
            color: outline ? (bg-color == transparent ? #495057 : bg-color) : text-color;
            vertical-alignment: center;
            horizontal-alignment: center;
        }
    }
}

export component DocLink inherits Rectangle {
    in property <string> label;
    in property <string> url;
    in property <string> description;

    callback clicked();

    min-height: 48px;
    background: touch.pressed ? #e9ecef : (touch.has-hover ? #f1f3f5 : transparent);
    border-radius: 4px;

    touch := TouchArea {
        clicked => {
            root.clicked();
        }
    }

    VerticalLayout {
        padding-left: 8px;
        padding-right: 8px;
        padding-top: 4px;
        padding-bottom: 4px;
        alignment: center;
        spacing: 2px;

        Text {
            text: label;
            font-size: 14px;
            font-weight: 600;
            color: #228be6;
        }
        Text {
            text: description;
            font-size: 12px;
            color: #868e96;
            wrap: word-wrap;
        }
    }
}