slint/tools/lsp/ui/components/console-panel.slint

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;
}
}
}
}
}