mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-20 15:22:13 +00:00
382 lines
13 KiB
Text
382 lines
13 KiB
Text
// Copyright © SixtyFPS GmbH info@slint.dev
|
|
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
|
|
|
import { Palette, ScrollView, ListView, Switch } from "std-widgets.slint";
|
|
import { Api, LogMessage, LogMessageLevel } from "../api.slint";
|
|
import { RecentColorPicker } from "widgets/floating-brush-sections/palettes.slint";
|
|
import { SimpleColumn } from "layout-helpers.slint";
|
|
import { ConsoleStyles, EditorSizeSettings, Icons, } from "styling.slint";
|
|
import {WindowGlobal } from "../windowglobal.slint";
|
|
|
|
|
|
export component ConsolePanel inherits SimpleColumn {
|
|
in-out property <bool> panel-expanded: false;
|
|
property <bool> dragging-up: false; // Track if currently dragging upward
|
|
property <length> panel-height: ConsoleStyles.log-height;
|
|
property <length> drag-start-height; // Track height at start of drag
|
|
property <length> previous-height: 300px; // Track previous panel height for restoration
|
|
property <bool> animate-panel: false; // Control animation state
|
|
property <bool> drag-started: false;
|
|
|
|
animate panel-height {
|
|
duration: animate-panel ? 200ms : 0ms;
|
|
easing: ease-out-quad;
|
|
}
|
|
changed panel-height => {
|
|
if !dragging-up && panel-height <= (-ConsoleStyles.header-height) {
|
|
panel-expanded = false;
|
|
}
|
|
}
|
|
Rectangle {
|
|
height: ConsoleStyles.header-height;
|
|
background: ConsoleStyles.header-background;
|
|
main-resize-handle := TouchArea {
|
|
y: 0;
|
|
height: 4px;
|
|
mouse-cursor: ns-resize;
|
|
moved => {
|
|
if !panel-expanded && self.mouse-y < self.pressed-y {
|
|
panel-expanded = true;
|
|
dragging-up = true;
|
|
drag-started = true;
|
|
panel-height = Math.max(30px, -(self.mouse-y - self.pressed-y));
|
|
} else if panel-expanded {
|
|
// Only update direction if we're actively moving
|
|
if !drag-started {
|
|
drag-started = true;
|
|
dragging-up = self.mouse-y < self.pressed-y;
|
|
}
|
|
|
|
// Resizing panel during drag
|
|
let max-height = WindowGlobal.window-height - 200px;
|
|
let new-height = panel-height - (self.mouse-y - self.pressed-y);
|
|
panel-height = Math.max((-ConsoleStyles.header-height), Math.min(max-height, new-height));
|
|
}
|
|
}
|
|
pointer-event(event) => {
|
|
if event.kind == PointerEventKind.down {
|
|
drag-started = false;
|
|
animate-panel = false; // Disable animation during drag
|
|
}
|
|
if event.kind == PointerEventKind.up {
|
|
animate-panel = true;
|
|
}
|
|
// Make close decision on release
|
|
if event.kind == PointerEventKind.up && panel-expanded {
|
|
|
|
if !dragging-up && panel-height <= 5px {
|
|
// Store current height before closing (if it's a reasonable size)
|
|
if panel-height > 50px {
|
|
previous-height = panel-height;
|
|
}
|
|
panel-height = (-ConsoleStyles.header-height);
|
|
}
|
|
if dragging-up && panel-height <= 30px {
|
|
panel-height = previous-height; // Restore previous height
|
|
}
|
|
// Also update previous-height when panel is at a good size
|
|
if panel-height > 50px {
|
|
previous-height = panel-height;
|
|
}
|
|
// Reset drag tracking
|
|
dragging-up = false;
|
|
drag-started = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
y: 0;
|
|
width: 100%;
|
|
height: 2px;
|
|
background: ConsoleStyles.divider-line;
|
|
opacity: main-resize-handle.has-hover ? 1.0 : 0.3;
|
|
}
|
|
|
|
Rectangle {
|
|
y: parent.height - self.height;
|
|
width: 100%;
|
|
height: 1px;
|
|
background: ConsoleStyles.divider-line;
|
|
opacity: 50%;
|
|
}
|
|
|
|
Rectangle {
|
|
y: parent.height - self.height;
|
|
width: 100%;
|
|
height: 1px;
|
|
background: ConsoleStyles.divider-line;
|
|
visible: panel-expanded;
|
|
}
|
|
|
|
label := Text {
|
|
x: 10px;
|
|
horizontal-alignment: left;
|
|
color: ConsoleStyles.text-color;
|
|
font-family: "Inter";
|
|
font-size: 12px;
|
|
text: @tr("Console");
|
|
}
|
|
|
|
last-message := Text {
|
|
x: label.width + label.x * 2;
|
|
width: min(self.preferred-width, parent.width - self.x - (parent.width - info-layout.x) - 20px);
|
|
overflow: elide;
|
|
horizontal-alignment: left;
|
|
color: ConsoleStyles.slint-blue;
|
|
font-family: "Source Code Pro";
|
|
font-size: 11px;
|
|
text: Api.log-output[Api.log-output.length - 1].message;
|
|
visible: !panel-expanded;
|
|
lm-ta := TouchArea {
|
|
mouse-cursor: MouseCursor.pointer;
|
|
clicked => {
|
|
if panel-expanded {
|
|
animate-panel = true;
|
|
panel-height = (-ConsoleStyles.header-height);
|
|
} else {
|
|
panel-height = previous-height;
|
|
panel-expanded = true;
|
|
animate-panel = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
HorizontalLayout {
|
|
alignment: end;
|
|
info-layout := HorizontalLayout {
|
|
spacing: 4px;
|
|
visible: Api.log-output.length > 0;
|
|
|
|
Image {
|
|
width: 14px;
|
|
source: Icons.info;
|
|
colorize: ConsoleStyles.slint-blue;
|
|
opacity: 0.9;
|
|
|
|
TouchArea {
|
|
mouse-cursor: MouseCursor.pointer;
|
|
clicked => {
|
|
panel-expanded = !panel-expanded;
|
|
}
|
|
}
|
|
}
|
|
|
|
Text {
|
|
text: Api.log-output.length;
|
|
vertical-alignment: center;
|
|
color: ConsoleStyles.text-color;
|
|
font-family: "Inter";
|
|
font-size: 12px;
|
|
TouchArea {
|
|
mouse-cursor: MouseCursor.pointer;
|
|
clicked => {
|
|
debug(parent.x);
|
|
panel-expanded = !panel-expanded;
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: 10px;
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: 36px;
|
|
Rectangle {
|
|
x: 0;
|
|
width: 1px;
|
|
height: 13px;
|
|
background: ConsoleStyles.text-color;
|
|
opacity: 0.3;
|
|
}
|
|
|
|
chevron-ta := TouchArea {
|
|
mouse-cursor: MouseCursor.pointer;
|
|
clicked => {
|
|
if panel-expanded {
|
|
animate-panel = true;
|
|
panel-height = (-ConsoleStyles.header-height);
|
|
} else {
|
|
panel-height = previous-height;
|
|
panel-expanded = true;
|
|
animate-panel = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
Image {
|
|
x: parent.width - self.width - 10px;
|
|
width: 16px;
|
|
height: 16px;
|
|
source: Icons.chevron-down;
|
|
colorize: ConsoleStyles.text-color;
|
|
opacity: chevron-ta.has-hover ? 1.0 : 0.5;
|
|
rotation-angle: panel-expanded ? 0deg : 180deg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if panel-expanded: Rectangle {
|
|
width: 100%;
|
|
height: ConsoleStyles.header-height;
|
|
background: ConsoleStyles.toolbar-background;
|
|
|
|
Rectangle {
|
|
x: 0;
|
|
width: 36px;
|
|
Image {
|
|
width: 14px;
|
|
source: Icons.clear;
|
|
colorize: ConsoleStyles.text-color;
|
|
opacity: cl-ta.has-hover ? 1.0 : 0.8;
|
|
cl-ta := TouchArea {
|
|
mouse-cursor: MouseCursor.pointer;
|
|
clicked => {
|
|
Api.clear-log-messages();
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
x: parent.width - self.width;
|
|
width: 1px;
|
|
height: 13px;
|
|
background: ConsoleStyles.text-color;
|
|
opacity: 0.3;
|
|
}
|
|
}
|
|
|
|
HorizontalLayout {
|
|
alignment: end;
|
|
Text {
|
|
text: @tr("Auto clear logs");
|
|
font-family: "Inter";
|
|
font-size: 12px;
|
|
color: ConsoleStyles.text-color;
|
|
vertical-alignment: center;
|
|
}
|
|
|
|
Rectangle {
|
|
width: 6px;
|
|
}
|
|
|
|
Switch {
|
|
checked <=> Api.auto-clear-console;
|
|
}
|
|
|
|
Rectangle {
|
|
width: 10px;
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
y: parent.height - self.height;
|
|
width: 100%;
|
|
height: 1px;
|
|
background: ConsoleStyles.divider-line;
|
|
}
|
|
}
|
|
|
|
if panel-expanded: Rectangle {
|
|
preferred-height: Math.min(panel-height, (WindowGlobal.window-height - 200px));
|
|
min-height: (-ConsoleStyles.header-height);
|
|
|
|
background: ConsoleStyles.log-background;
|
|
|
|
// Scroll to end workaround while this feature is missing from ScrollView
|
|
property <int> timer-clicked: 0;
|
|
function scroll-to-bottom() {
|
|
lv.viewport-y = -100000px;
|
|
timer-clicked = 0;
|
|
scroll-timer.running = true;
|
|
}
|
|
|
|
scroll-timer := Timer {
|
|
running: true;
|
|
interval: 1ms;
|
|
triggered => {
|
|
lv.viewport-y = -100000px;
|
|
if timer-clicked < 1 {
|
|
timer-clicked += 1;
|
|
} else {
|
|
self.running = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
property <int> scroll-to-end-trigger: Api.log-output.length;
|
|
changed scroll-to-end-trigger => {
|
|
scroll-to-bottom();
|
|
}
|
|
|
|
lv := ListView {
|
|
for lm[index] in Api.log-output: Rectangle {
|
|
height: helper-message.height + 6px;
|
|
// helper-message is used to calculate the height of the message
|
|
// as TextInput gets into a binding loop if used alone.
|
|
helper-message := Text {
|
|
x: 10px;
|
|
width: message.width;
|
|
text: lm.message;
|
|
font-family: message.font-family;
|
|
font-size: message.font-size;
|
|
horizontal-alignment: message.horizontal-alignment;
|
|
font-weight: message.font-weight;
|
|
wrap: message.wrap;
|
|
visible: false;
|
|
}
|
|
|
|
message := TextInput {
|
|
x: 10px;
|
|
width: parent.width - 80px;
|
|
read-only: true;
|
|
text: lm.message;
|
|
color: lm.level == LogMessageLevel.Error ? ConsoleStyles.slint-blue : ConsoleStyles.text-color;
|
|
font-family: "Source Code Pro";
|
|
font-size: 11px;
|
|
horizontal-alignment: left;
|
|
vertical-alignment: center;
|
|
font-weight: 200;
|
|
wrap: TextWrap.word-wrap;
|
|
}
|
|
|
|
if !lm.file.is-empty: Rectangle {
|
|
width: 40px;
|
|
height: 14px;
|
|
x: parent.width - self.width - EditorSizeSettings.standard-margin;
|
|
file-link := Text {
|
|
text: "\{lm.line}:\{lm.column}";
|
|
color: ConsoleStyles.text-color;
|
|
font-family: "Source Code Pro";
|
|
font-size: 11px;
|
|
}
|
|
|
|
Rectangle {
|
|
y: parent.height - self.height;
|
|
width: file-link.width;
|
|
height: 1px;
|
|
background: ConsoleStyles.text-color;
|
|
opacity: ta.has-hover ? 1.0 : 0.5;
|
|
}
|
|
|
|
ta := TouchArea {
|
|
mouse-cursor: MouseCursor.pointer;
|
|
clicked => {
|
|
Api.show-document(lm.file, lm.line, lm.column);
|
|
}
|
|
}
|
|
}
|
|
Rectangle {
|
|
y: parent.height - self.height;
|
|
height: 1px;
|
|
background: ConsoleStyles.text-color;
|
|
opacity: 0.2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|