// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: MIT global Palette { out property window-background: #eee; out property widget-background: #ddd; out property widget-stroke: #888; out property window-border: #ccc; out property text-color: #666; out property hyper-blue: #90d1ff; } //------ MdiWindow ---- component MdiWindow inherits Rectangle { in property title; in-out property window-x <=> window.x; in-out property window-y <=> window.y; in-out property is-open: true; width: 100%; height: 100%; window := Rectangle { property open-width: l.preferred-width; property open-height: l.preferred-height; x:0; y:0; background: Palette.window-background; border-width: 2px; border-color: Palette.window-border; border-radius: 6px; drop-shadow-blur: 25px; drop-shadow-color: Palette.window-border; width: l.preferred-width; height: l.preferred-height - hidden.preferred-height; clip: true; states [ open when root.is-open : { width: self.open-width; height: self.open-height; expand.angle: 0deg; in { animate width, height, expand.angle { duration: 150ms; easing: ease; } } out { animate width, height, expand.angle { duration: 150ms; easing: ease; } } } ] TouchArea {} l := VerticalLayout { padding: window.border-width; alignment: root.is-open ? stretch : start; title_bar := TouchArea { moved => { if (self.pressed) { root.window-x += self.mouse-x - self.pressed-x; root.window-y += self.mouse-y - self.pressed-y; } } HorizontalLayout { padding: window.border-width; spacing: window.border-width * 2; expand := TouchArea { clicked => { root.is-open = !root.is-open; } property angle: -90deg; width: 30px; Path { stroke-width: window.border-width * (expand.has-hover ? 1.5 : 1); stroke: parent.has-hover ? Palette.widget-stroke.darker(100%) : Palette.widget-stroke; viewbox-x: -1.5; viewbox-y: -1.5; viewbox-height: 3; viewbox-width: 3; MoveTo { x: cos(expand.angle) * -1 - sin(expand.angle) * -1; y: sin(expand.angle) * -1 + cos(expand.angle) * -1; } LineTo { x: cos(expand.angle) * 1 - sin(expand.angle) * -1; y: sin(expand.angle) * 1 + cos(expand.angle) * -1; } LineTo { x: cos(expand.angle) * 0 - sin(expand.angle) * 1; y: sin(expand.angle) * 0 + cos(expand.angle) * 1; } LineTo { x: cos(expand.angle) * -1 - sin(expand.angle) * -1; y: sin(expand.angle) * -1 + cos(expand.angle) * -1; } } } Text { text: root.title; horizontal-alignment: center; color: Palette.text-color; } close_button := TouchArea { clicked => { root.visible = false; } width: 30px; Path { stroke-width: window.border-width * (close-button.has-hover ? 1.5 : 1); stroke: parent.has-hover ? Palette.widget-stroke.darker(100%) : Palette.widget-stroke; viewbox-x: -1.5; viewbox-y: -1.5; viewbox-height: 3; viewbox-width: 3; MoveTo { x: -1; y: -1; } LineTo { x: 1; y: 1; } MoveTo { x: -1; y: 1; } LineTo { x: 1; y: -1; } } } } } hidden := VerticalLayout { visible: root.is-open; Rectangle { height: window.border-width; background: window.border-color; } @children } } if root.is-open : resize-handle := TouchArea { moved => { if (self.pressed) { window.open-width = max(l.min-width, min(l.max-width, window.open-width + self.mouse-x - self.pressed-x)); window.open-height = max(l.min-height, min(l.max-height, window.open-height + self.mouse-y - self.pressed-y)); } } width: 20px; height: self.width; x: parent.width - self.width; y: parent.height - self.height; mouse-cursor: MouseCursor.nwse-resize; Path { stroke-width: window.border-width; stroke: Palette.window-border; viewbox-height: 1.2; viewbox-width: 1.2; MoveTo { x: 0; y: 1; } LineTo { x: 1; y: 0; } MoveTo { x: 0.4; y: 1; } LineTo { x: 1; y: 0.4; } MoveTo { x: 0.8; y: 1; } LineTo { x: 1; y: 0.8; } } } } } //------ Widgets ------ import {LineEdit, TextEdit, ComboBox, GridBox, VerticalBox, HorizontalBox} from "std-widgets.slint"; component Label inherits Text { color: Palette.text-color; } component Button inherits TouchArea { in property text <=> t.text; min-height: t.min-height; min-width: t.min-width + 10px; Rectangle { border-width: 1.5px; border-color: root.has-hover ? Palette.widget-stroke : transparent; border-radius: 3px; background: root.pressed ? Palette.widget-background.darker(30%) : Palette.widget-background; t := Label { y:0; width: 100%; horizontal-alignment: center; } } } component CheckBox inherits TouchArea { in property text <=> t.text; in-out property checked; clicked => { root.checked = !root.checked; } HorizontalLayout { spacing: 5px; VerticalLayout { alignment: center; Rectangle { width: 20px; height: 20px; border-width: 1.5px; border-color: root.has-hover ? Palette.widget-stroke : transparent; border-radius: 3px; background: root.pressed ? Palette.widget-background.darker(30%) : Palette.widget-background; if root.checked : Path { stroke: root.has-hover ? Palette.widget-stroke.darker(100%) : Palette.widget-stroke; stroke-width: root.pressed ? 2.5px : 2px; viewbox-height: 1; viewbox-width: 1; MoveTo { x: 0.2; y: 0.5; } LineTo { x: 0.5; y: 0.8; } LineTo { x: 0.8; y: 0.2; } } } } t := Label { } } } component RadioButton inherits TouchArea { in property text <=> t.text; in-out property checked; HorizontalLayout { spacing: 5px; VerticalLayout { alignment: center; Rectangle { width: 20px; height: 20px; border-width: 1.5px; border-color: root.has-hover ? Palette.widget-stroke : transparent; border-radius: self.width / 2; background: root.pressed ? Palette.widget-background.darker(30%) : Palette.widget-background; if root.checked : Rectangle { background: root.has-hover ? Palette.widget-stroke.darker(100%) : Palette.widget-stroke; border-radius: self.width / 2; width: parent.width / 2; height: parent.width / 2; x: parent.width / 4; y: parent.width / 4; } } } t := Label { } } } component SelectableLabel inherits TouchArea { in-out property checked; in-out property text <=> t.text; min-height: t.min-height; min-width: t.min-width + 10px; Rectangle { border-width: 1.5px; border-color: root.has-hover ? Palette.widget-stroke : transparent; border-radius: 3px; background: root.checked ? Palette.hyper-blue : root.pressed ? Palette.widget-background.darker(30%) : root.has-hover ? Palette.widget-background : transparent; t := Label { y:0; width: 100%; horizontal-alignment: center; } } } component Slider inherits Rectangle { in property enabled <=> touch.enabled; in property maximum: 100; in property minimum: 0; in-out property value; callback changed(float); min-height: 24px; min-width: 100px; horizontal-stretch: 1; vertical-stretch: 0; Rectangle { width: parent.width; height: parent.height / 2; y: (parent.height - self.height) / 2; border-radius: 2px; background: Palette.widget-background; } handle := Rectangle { width: self.height; height: parent.height; border-radius: self.height / 2; border-color: touch.has-hover ? Palette.widget-stroke.darker(100%) : Palette.widget-stroke; border-width: touch.pressed ? 4px : touch.has-hover ? 3px : 2px; background: touch.pressed ? Palette.widget-background.darker(30%) : Palette.widget-background; x: (root.width - handle.width) * max(0, min(1, (root.value - root.minimum)/(root.maximum - root.minimum))); } touch := TouchArea { pointer-event(event) => { if (event.button == PointerEventButton.left && event.kind == PointerEventKind.down) { self.pressed-value = root.value; } } moved => { if (self.enabled && self.pressed) { root.value = max(root.minimum, min(root.maximum, self.pressed-value + (touch.mouse-x - touch.pressed-x) * (root.maximum - root.minimum) / (root.width - handle.width))); root.changed(root.value); } } property pressed-value; width: parent.width; height: parent.height; } } component Hyperlink inherits Text { in-out property link; color: Palette.hyper-blue; TouchArea { mouse-cursor: pointer; } } component DragValue inherits TouchArea { in-out property value; in-out property _pressed-value; moved => { if (root.pressed) { root.value = root._pressed-value +(root.mouse-x - root.pressed-x) / 2px; } } pointer-event(e) => { if (e.kind == PointerEventKind.down) { root._pressed-value = root.value; } } min-height: t.min-height; min-width: Math.max(t.min-width + 10px, 50px); mouse-cursor: MouseCursor.ew-resize; Rectangle { border-width: 1.5px; border-color: root.has-hover ? Palette.widget-stroke : transparent; border-radius: 3px; background: root.pressed ? Palette.widget-background.darker(30%) : Palette.widget-background; t := Label { y:0; width: 100%; horizontal-alignment: center; text: round(root.value); } } } component ProgressBar inherits Rectangle { in-out property value; min-height: 24px; min-width: 100px; background: Palette.window-background; border-radius: root.height / 2; Rectangle { x:0; height: 100%; width: self.height + (parent.width - self.height) * max(0, min(1, root.value / 100)); border-radius: self.height / 2; background: Palette.hyper-blue; } Label { height: 100%; vertical-alignment: center; x: self.height/2; text: round(root.value) + "%"; } } //------ Demo apps ------- component Gallery inherits GridBox { function unsel() { r1.checked = false; r2.checked = false; r3.checked = false; } Row { Text { text: "Label:"; } Label { text: "Welcome to the widget gallery!"; } } Row { Text { text: "Hyperlink:"; } Hyperlink { text: "Slint homepage"; link: "https://slint.dev"; } } Row { Text { text: "TextEdit:"; } LineEdit { placeholder-text: "WriteSomething here";} } Row { Text { text: "Button:"; } HorizontalLayout { alignment: start; Button { text: "Click me!"; clicked => { cb.checked = !cb.checked; } } } } Row { Text { text: "Checkbox:"; } cb := CheckBox { text: "Checkbox"; } } Row { Text { text: "RadioButton:"; } HorizontalBox { alignment: start; r1 := RadioButton { text: "First"; clicked => { root.unsel(); r1.checked = true; } } r2 := RadioButton { text: "Second"; clicked => { root.unsel(); r2.checked = true; } } r3 := RadioButton { text: "Third"; clicked => { root.unsel(); r3.checked = true; } } } } Row { Text { text: "SelectableLabel:"; } HorizontalBox { alignment: start; SelectableLabel { text: "First"; checked <=> r1.checked; clicked => { root.unsel(); r1.checked = true; } } SelectableLabel { text: "Second"; checked <=> r2.checked; clicked => { root.unsel(); r2.checked = true; } } SelectableLabel { text: "Third"; checked <=> r3.checked; clicked => { root.unsel(); r3.checked = true; } } } } Row { Text { text: "ComboBox:"; } HorizontalBox { alignment: start; ComboBox { selected => { r1.checked = self.current-index == 0; r2.checked = self.current-index == 1; r3.checked = self.current-index == 2; } model: ["First", "Second", "Third"]; } Label { text: "Take your pick"; } } } Row { Text { text: "Slider:"; } sl := Slider { } } Row { Text { text: "DragValue:"; } HorizontalLayout { alignment: start; DragValue { value <=> sl.value; } } } Row { Text { text: "ProgressBar:"; } ProgressBar { value <=> sl.value; } } Rectangle {} } component TextEditDemo inherits VerticalLayout { preferred-height: 150px; preferred-width: 300px; TextEdit { text: "Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum"; } } export component Demo inherits Window { preferred-width: 1024px; preferred-height: 800px; background: white; w1 := MdiWindow { x:0;y:0; title: "🗄 Widget Gallery"; window-x: 30px; window-y: 20px; Gallery { } } w2 := MdiWindow { x:0;y:0; visible: false; title: "🗉 TextEdit"; window-x: 230px; window-y: 520px; TextEditDemo { } } side-panel := Rectangle { border-color: resize-side-panel.has-hover ? Palette.window-border.darker(100%) : Palette.window-border; border-width: 2px; background: Palette.window-background; x: parent.width - self.width; width: side-panel-l.preferred-width; height: 100%; side-panel-l := VerticalBox { alignment: start; Label { font-weight: 500; text: "Slint Demos"; horizontal-alignment: center; } Rectangle { height: 2px; background: Palette.window-border; } Label { preferred-width: 0px; text: "This is a demo which is based on the demo from the egui framework"; wrap: word-wrap; horizontal-alignment: center; } Rectangle { height: 2px; background: Palette.window-border; } CheckBox { text: w1.title; checked <=> w1.visible; } CheckBox { text: w2.title; checked <=> w2.visible; } } resize-side-panel := TouchArea { moved => { if (self.pressed) { side-panel.width = max(side-panel-l.min-width, min(root.width, side-panel.width - (self.mouse-x - self.pressed-x))); } } x:0; height: 100%; width: 4px; mouse-cursor: ew-resize; } } }