slint/tools/lsp/ui/windowglobal.slint
Nigel Breslaw 09dae48828
Some checks failed
autofix.ci / format_fix (push) Has been cancelled
autofix.ci / lint_typecheck (push) Has been cancelled
CI / files-changed (push) Has been cancelled
CI / build_and_test (--exclude bevy-example, ubuntu-22.04, 1.85) (push) Has been cancelled
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, --exclude bevy-example, windows-2022, 1.85) (push) Has been cancelled
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, macos-14, stable) (push) Has been cancelled
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, beta) (push) Has been cancelled
CI / build_and_test (--exclude ffmpeg --exclude gstreamer-player, windows-2022, stable) (push) Has been cancelled
CI / build_and_test (ubuntu-22.04, nightly) (push) Has been cancelled
CI / node_test (macos-14) (push) Has been cancelled
CI / node_test (windows-2022) (push) Has been cancelled
CI / cpp_test_driver (ubuntu-22.04) (push) Has been cancelled
CI / cpp_package_test (push) Has been cancelled
CI / vsce_build_test (push) Has been cancelled
CI / mcu (pico-st7789, thumbv6m-none-eabi) (push) Has been cancelled
CI / mcu (pico2-st7789, thumbv8m.main-none-eabihf) (push) Has been cancelled
CI / mcu (stm32h735g, thumbv7em-none-eabihf) (push) Has been cancelled
CI / mcu-embassy (push) Has been cancelled
CI / wasm_demo (push) Has been cancelled
CI / tree-sitter (push) Has been cancelled
CI / updater_test (0.3.0) (push) Has been cancelled
CI / esp-idf-quick (push) Has been cancelled
CI / android (push) Has been cancelled
CI / miri (push) Has been cancelled
CI / test-figma-inspector (push) Has been cancelled
CI / material-components (push) Has been cancelled
CI / node_test (ubuntu-22.04) (push) Has been cancelled
CI / python_test (macos-14) (push) Has been cancelled
CI / python_test (ubuntu-22.04) (push) Has been cancelled
CI / python_test (windows-2022) (push) Has been cancelled
CI / cpp_test_driver (macos-14) (push) Has been cancelled
CI / cpp_test_driver (windows-2022) (push) Has been cancelled
CI / cpp_cmake (macos-14, 1.85) (push) Has been cancelled
CI / cpp_cmake (ubuntu-22.04, stable) (push) Has been cancelled
CI / cpp_cmake (windows-2022, nightly) (push) Has been cancelled
CI / ffi_32bit_build (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / wasm (push) Has been cancelled
CI / fmt_test (push) Has been cancelled
Color Picker: Support @conic-gradient (#9592)
2025-10-02 10:38:59 +02:00

425 lines
15 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 {
Api,
BrushKind,
ElementInformation,
GradientStop,
PreviewData,
PropertyInformation,
PropertyValue,
PropertyValueKind,
PropertyValueTable,
} from "./api.slint";
import { StatusLineApi } from "components/status-line.slint";
export enum WidgetMode { edit, preview, color-stop }
export enum PickerTab {
color,
gradient,
css-color,
globals
}
// Color and Brush properties are related, but while a <brush> can be a color,
// a <color> property cannot be a brush. This property type is used to ensure
// <color> properties do not show the more complex brush editor.
export enum BrushPropertyType {
color,
brush,
}
export enum BrushMode {
color,
conic,
linear,
radial,
css-color,
code,
}
export enum GradientType {
conic,
linear,
radial,
}
export enum ColorCodeType {
other,
css-color,
global
}
export global WindowGlobal {
in-out property <length> window-width;
in-out property <length> window-height;
}
export global TableData {
callback populate-table(property-group-id: string, preview-data: PreviewData);
callback show-brush-editor(selected-row: int, selected-col: int);
callback set-color-preview(value: string);
out property <string> property-group-id;
out property <PreviewData> preview-data;
out property <PropertyValueTable> current-table;
property <string> possible-error;
property <int> selected-row;
property <int> selected-col;
show-brush-editor(selected-row, selected-col) => {
self.selected-row = selected-row;
self.selected-col = selected-col;
WindowManager.show-floating-preview-widget(self.property-group-id, self.preview-data, self.current-table.values[selected-row][selected-col]);
}
populate-table(property-group-id, preview-data) => {
self.property-group-id = property-group-id;
self.preview-data = preview-data;
self.current-table = Api.get-property-value-table(property-group-id, preview-data.name);
}
set-color-preview(value) => {
self.current-table.values[self.selected-row][self.selected-col].was-edited = true;
self.current-table.values[self.selected-row][self.selected-col].edited-value = value;
self.possible_error = Api.set-property-value-table(root.property-group-id, root.preview-data.name, root.current-table.values, root.current-table.is-array);
StatusLineApi.help-text = self.possible-error;
if self.possible-error == "" {
self.populate-table(root.property-group-id, root.preview-data);
}
}
}
export global PickerData {
out property <PickerTab> active-tab: PickerTab.color;
out property <BrushPropertyType> picker-mode: color;
out property <BrushMode> brush-mode: color;
// Used to remember the last used gradient type so it won't change if the user
// switches Picker tab
in-out property <GradientType> last-used-gradient-type: linear;
in-out property <float> hue;
in-out property <float> saturation;
in-out property <float> value;
// alpha is an int, instead of float to help snap values to whole percentage numbers
in-out property <int> alpha;
out property <color> current-color: hsv(hue, saturation, value, alpha / 100);
in-out property <BrushKind> current-brush-kind;
// As the brush can be updated via the Api.move-gradient-stop function it won't trigger a reactive update.
// So we need to keep track of the current brush ourselves and update it with update-brush();
out property <brush> current-brush;
in-out property <[GradientStop]> current-gradient-stops;
in-out property <float> current-angle;
in-out property <int> current-stop-index: 0;
property <float> current-stop-position: current-gradient-stops[current-stop-index].position;
property <color> current-stop-color: current-gradient-stops[current-stop-index].color;
callback rebuild-gradient-stops();
callback set-active-tab(picker-tab: PickerTab);
callback set-gradient-type(gradient-type: GradientType);
callback reset();
changed current-stop-index => {
update-brush();
if WindowManager.showing-color-stop-picker {
set-current-stop-as-color();
}
}
changed current-color => {
update-brush();
if WindowManager.showing-color-stop-picker {
current-gradient-stops[current-stop-index].color = current-color;
} else if WindowManager.widget-mode == WidgetMode.preview && current-brush-kind == BrushKind.solid {
WindowManager.update-preview-value(Api.color-to-data(root.current-color).text);
}
}
changed current-brush-kind => {
update-brush();
if WindowManager.widget-mode == WidgetMode.preview {
WindowManager.update-brush-preview();
}
}
changed current-angle => {
if WindowManager.widget-mode == WidgetMode.preview {
update-brush();
WindowManager.update-brush-preview();
}
}
changed current-stop-position => {
update-brush();
WindowManager.update-brush-preview();
}
changed current-stop-color => {
update-brush();
WindowManager.update-brush-preview();
}
changed active-tab => {
WindowManager.hide-color-stop-picker();
}
rebuild-gradient-stops => {
current-gradient-stops = Api.clone-gradient-stops(current-gradient-stops);
update-brush();
WindowManager.update-brush-preview();
}
set-active-tab(picker-tab) => {
active-tab = picker-tab;
if picker-tab == PickerTab.color {
current-brush-kind = BrushKind.solid;
brush-mode = BrushMode.color;
}
if picker-tab == PickerTab.gradient {
if last-used-gradient-type == GradientType.radial {
current-brush-kind = BrushKind.radial;
brush-mode = BrushMode.radial;
} else if last-used-gradient-type == GradientType.conic {
current-brush-kind = BrushKind.conic;
brush-mode = BrushMode.conic;
} else {
current-brush-kind = BrushKind.linear;
brush-mode = BrushMode.linear;
}
}
}
set-gradient-type(gradient-type) => {
last-used-gradient-type = gradient-type;
if gradient-type == GradientType.linear {
current-brush-kind = BrushKind.linear;
brush-mode = BrushMode.linear;
} else if gradient-type == GradientType.conic {
current-brush-kind = BrushKind.conic;
brush-mode = BrushMode.conic;
} else {
current-brush-kind = BrushKind.radial;
brush-mode = BrushMode.radial;
}
}
reset => {
brush-mode = BrushMode.color;
active-tab = PickerTab.color;
picker-mode = BrushPropertyType.color;
last-used-gradient-type = GradientType.linear;
}
public function set-current-stop-as-color() {
set-color(current-gradient-stops[current-stop-index].color);
}
public function update-brush() {
self.current-brush = Api.create-brush(current-brush-kind, current-angle, current-color, current-gradient-stops);
}
function set-svg-or-global-tab(property-value: PropertyValue) {
if PickerData.get-color-code-type(property-value) == ColorCodeType.css-color {
set-active-tab(PickerTab.css-color);
} else {
set-active-tab(PickerTab.globals);
}
}
public function init-with-property-value(property-value: PropertyValue) {
PickerData.current-brush-kind = property-value.brush-kind;
current-gradient-stops = Api.clone-gradient-stops(property-value.gradient-stops);
current-angle = property-value.value-float;
brush-mode = property-value.brush-kind == BrushKind.solid ? BrushMode.color : property-value.brush-kind == BrushKind.linear ? BrushMode.linear : BrushMode.radial;
if property-value.kind == PropertyValueKind.color {
picker-mode = BrushPropertyType.color;
if PickerData.get-color-code-type(property-value) == ColorCodeType.other {
set-active-tab(PickerTab.color);
} else {
set-svg-or-global-tab(property-value);
}
}
if property-value.kind == PropertyValueKind.brush {
picker-mode = BrushPropertyType.brush;
if PickerData.get-color-code-type(property-value) == ColorCodeType.other {
if property-value.brush-kind == BrushKind.solid {
set-active-tab(PickerTab.color);
} else {
set-active-tab(PickerTab.gradient);
}
} else {
set-svg-or-global-tab(property-value);
}
}
if property-value.brush-kind == BrushKind.solid {
if property-value.code == "" {
PickerData.set-default-color();
} else {
PickerData.set-color(property-value.value-brush);
}
} else {
PickerData.set-color(PickerData.current-gradient-stops[PickerData.current-stop-index].color);
if property-value.brush-kind == BrushKind.radial {
set-gradient-type(GradientType.radial)
}
if property-value.brush-kind == BrushKind.linear {
set-gradient-type(GradientType.linear)
}
if property-value.brush-kind == BrushKind.conic {
set-gradient-type(GradientType.conic)
}
}
update-brush();
}
public function set-color(color: color) {
PickerData.hue = color.to-hsv().hue;
PickerData.saturation = color.to-hsv().saturation;
PickerData.value = color.to-hsv().value;
PickerData.alpha = color.to-hsv().alpha * 100;
}
public function set-default-color() {
if Api.recent-colors.length > 0 {
set-color(Api.recent-colors[0]);
} else {
set-color(#2479f4);
}
}
pure public function get-color-code-type(property-value: PropertyValue) -> ColorCodeType {
if property-value.value-string == "" {
return ColorCodeType.other;
}
if Api.is-css-color(property-value.code) {
return ColorCodeType.css-color;
} else {
return ColorCodeType.global;
}
}
}
export global WindowManager {
out property <bool> showing-color-picker: false;
out property <bool> showing-table-editor: false;
out property <bool> showing-color-stop-picker: false;
out property <WidgetMode> widget-mode: edit;
property <ElementInformation> current-element-information;
out property <PropertyInformation> current-property-information;
in-out property <string> current-property-container-id;
in-out property <PreviewData> current-preview-data;
property component-factory <=> Api.preview-data;
property <string> possible_error;
property <string> brush-string;
callback show-floating-widget(property-information: PropertyInformation, element-information: ElementInformation);
callback show-floating-preview-widget(property-container-id: string, preview-data: PreviewData, property-value: PropertyValue);
callback show-floating-table-editor(property-group-id: string, preview-data: PreviewData);
callback hide-floating-widget();
callback hide-floating-color-widget();
callback apply-current-value(value: string);
callback update-preview-value(value: string);
callback update-brush-preview();
callback show-color-stop-picker();
callback hide-color-stop-picker();
show-floating-widget(property-information, element-information) => {
widget-mode = WidgetMode.edit;
current-property-information = property-information;
current-element-information = element-information;
PickerData.init-with-property-value(current-property-information.value);
showing-color-picker = true;
}
hide-floating-widget => {
showing-color-picker = false;
showing-table-editor = false;
showing-color-stop-picker = false;
current-element-information = { };
current-property-information = { };
widget-mode = WidgetMode.edit;
PickerData.reset()
}
show-floating-preview-widget(property-container-id, preview-data, property-value) => {
current-property-container-id = property-container-id;
current-preview-data = preview-data;
widget-mode = WidgetMode.preview;
PickerData.init-with-property-value(property-value);
showing-color-picker = true;
}
show-floating-table-editor(property-group-id, preview-data) => {
TableData.populate-table(property-group-id, preview-data);
showing-table-editor = true;
}
show-color-stop-picker => {
PickerData.set-current-stop-as-color();
showing-color-stop-picker = true;
}
hide-color-stop-picker => {
showing-color-stop-picker = false;
}
apply-current-value(text) => {
Api.set-code-binding(
current-element-information.source-uri,
current-element-information.source-version,
current-element-information.offset,
current-property-information.name,
text);
}
update-preview-value(text) => {
if WindowManager.showing-table-editor {
TableData.set-color-preview("\"\{text}\"");
} else {
self.possible_error = Api.set-json-preview-data(current-property-container-id, current-preview-data.name, "\"\{text}\"", check-new-value(current-property-container-id + current-preview-data.name));
}
}
update-brush-preview() => {
if widget-mode == WidgetMode.preview {
brush-string = Api.as-slint-brush(PickerData.current-brush-kind, PickerData.current-angle, PickerData.current-brush, PickerData.current-gradient-stops);
if WindowManager.showing-table-editor {
TableData.set-color-preview("\"\{brush-string}\"");
} else {
self.possible_error = Api.set-json-preview-data(current-property-container-id, current-preview-data.name, "\"\{brush-string}\"", check-new-value(current-property-container-id + current-preview-data.name));
}
}
}
hide-floating-color-widget() => {
if showing-table-editor {
showing-color-picker = false;
showing-color-stop-picker = false;
} else {
hide-floating-widget();
}
}
property <string> container-property-name;
function check-new-value(value: string) -> bool {
if container-property-name == value {
return false;
} else {
container-property-name = value;
return true;
}
}
}