Content-Length: 544133 | pFad | http://github.com/slint-ui/slint/pull/7859/commits/9db858f0281a57aab9b36ac008f158d718b6c86f

8B Add a brush widgets into the live-preview by hunger · Pull Request #7859 · slint-ui/slint · GitHub
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a brush widgets into the live-preview #7859

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Prev Previous commit
Next Next commit
live-preview: Add rudimentary BrushWidget
... and add helper code to enable the backend to set/show
Brush information.
  • Loading branch information
hunger committed Mar 14, 2025
commit 9db858f0281a57aab9b36ac008f158d718b6c86f
254 changes: 210 additions & 44 deletions tools/lsp/preview/ui.rs
Original file line number Diff line number Diff line change
@@ -104,25 +104,12 @@ pub fn create_ui(style: String, experimental: bool) -> Result<PreviewUi, Platfor
api.on_string_to_code(string_to_code);
api.on_string_to_color(|s| string_to_color(s.as_ref()).unwrap_or_default());
api.on_string_is_color(|s| string_to_color(s.as_ref()).is_some());
api.on_color_to_data(|c| {
let encoded = c.as_argb_encoded();

let a = ((encoded & 0xff000000) >> 24) as u8;
let r = ((encoded & 0x00ff0000) >> 16) as u8;
let g = ((encoded & 0x0000ff00) >> 8) as u8;
let b = (encoded & 0x000000ff) as u8;

ColorData {
a: a as i32,
r: r as i32,
g: g as i32,
b: b as i32,
text: format!(
"#{:08x}",
((r as u32) << 24) + ((g as u32) << 16) + ((b as u32) << 8) + (a as u32)
)
.into(),
}
api.on_color_to_data(|c| ColorData {
a: c.alpha() as i32,
r: c.red() as i32,
g: c.green() as i32,
b: c.blue() as i32,
text: color_to_string(c).into(),
});
api.on_rgba_to_color(|r, g, b, a| {
if (0..256).contains(&r)
@@ -136,6 +123,28 @@ pub fn create_ui(style: String, experimental: bool) -> Result<PreviewUi, Platfor
}
});

api.on_as_json_brush(as_json_brush);
api.on_as_slint_brush(as_slint_brush);
api.on_create_brush(create_brush);
api.on_add_gradient_stop(|model, row, value| {
if row < 0 {
return;
}
let row = row as usize;
if row <= model.row_count() {
model.as_any().downcast_ref::<slint::VecModel<_>>().unwrap().insert(row, value);
}
});
api.on_remove_gradient_stop(|model, row| {
if row < 0 {
return;
}
let row = row as usize;
if row < model.row_count() {
model.as_any().downcast_ref::<slint::VecModel<_>>().unwrap().remove(row as usize)
}
});

#[cfg(target_vendor = "apple")]
api.set_control_key_name("command".into());

@@ -423,6 +432,15 @@ fn string_to_code(
.into()
}

fn color_to_string(color: slint::Color) -> String {
let a = color.alpha();
let r = color.red();
let g = color.green();
let b = color.blue();

format!("#{r:02x}{g:02x}{b:02x}{a:02x}")
}

fn string_to_color(text: &str) -> Option<slint::Color> {
literals::parse_color_literal(text).map(slint::Color::from_argb_encoded)
}
@@ -484,6 +502,7 @@ fn set_default_brush(
return;
}
}
value.brush_kind = BrushKind::Solid;
let text = "#00000000";
let color = literals::parse_color_literal(text).unwrap();
value.value_string = text.into();
@@ -872,6 +891,23 @@ fn map_value_and_type(
value: &Option<slint_interpreter::Value>,
mapping: &mut ValueMapping,
) {
fn map_color(
mapping: &mut ValueMapping,
color: slint::Color,
kind: PropertyValueKind,
code: slint::SharedString,
) {
let color_string = color_to_string(color);
mapping.headers.push(mapping.name_prefix.clone());
mapping.current_values.push(PropertyValue {
kind,
brush_kind: BrushKind::Solid,
value_brush: slint::Brush::SolidColor(color),
value_string: color_string.into(),
code,
..Default::default()
});
}
use i_slint_compiler::expression_tree::Unit;
use langtype::Type;

@@ -985,23 +1021,62 @@ fn map_value_and_type(
});
}
Type::Color => {
let color = get_value::<slint::Color>(value);
let color_string = {
let a = color.alpha();
let r = color.red();
let g = color.green();
let b = color.blue();

format!("#{r:02x}{g:02x}{b:02x}{a:02x}")
};
mapping.headers.push(mapping.name_prefix.clone());
mapping.current_values.push(PropertyValue {
kind: PropertyValueKind::Color,
value_brush: slint::Brush::SolidColor(color),
value_string: color_string.into(),
code: get_code(value),
..Default::default()
});
map_color(
mapping,
get_value::<slint::Color>(value),
PropertyValueKind::Color,
get_code(value),
);
}
Type::Brush => {
let brush = get_value::<slint::Brush>(value);
match brush {
slint::Brush::SolidColor(c) => {
map_color(mapping, c, PropertyValueKind::Brush, get_code(value))
}
slint::Brush::LinearGradient(lg) => {
mapping.headers.push(mapping.name_prefix.clone());
mapping.current_values.push(PropertyValue {
kind: PropertyValueKind::Brush,
brush_kind: BrushKind::Linear,
value_float: lg.angle(),
gradient_stops: Rc::new(slint::VecModel::from(
lg.stops()
.map(|gs| GradientStop { color: gs.color, position: gs.position })
.collect::<Vec<_>>(),
))
.into(),

code: get_code(value),
..Default::default()
});
}
slint::Brush::RadialGradient(rg) => {
mapping.headers.push(mapping.name_prefix.clone());
mapping.current_values.push(PropertyValue {
kind: PropertyValueKind::Brush,
brush_kind: BrushKind::Radial,
gradient_stops: Rc::new(slint::VecModel::from(
rg.stops()
.map(|gs| GradientStop { color: gs.color, position: gs.position })
.collect::<Vec<_>>(),
))
.into(),

code: get_code(value),
..Default::default()
});
}
_ => {
mapping.headers.push(mapping.name_prefix.clone());
mapping.current_values.push(PropertyValue {
kind: PropertyValueKind::Code,
value_string: "???".into(),
code: get_code(value),
..Default::default()
});
}
}
}
Type::Bool => {
mapping.headers.push(mapping.name_prefix.clone());
@@ -1099,12 +1174,7 @@ fn map_value_and_type(
}
}
}
Type::Image
| Type::Model
| Type::PathData
| Type::Easing
| Type::Brush
| Type::UnitProduct(_) => {
Type::Image | Type::Model | Type::PathData | Type::Easing | Type::UnitProduct(_) => {
mapping.headers.push(mapping.name_prefix.clone());
mapping.is_too_complex = true;
}
@@ -1285,8 +1355,11 @@ fn set_json_preview_data(
) -> SharedString {
let property_name = (!property_name.is_empty()).then_some(property_name.to_string());

let Ok(json) = serde_json::from_str::<serde_json::Value>(json_string.as_ref()) else {
return SharedString::from("Input is not valid JSON");
let json = match serde_json::from_str::<serde_json::Value>(json_string.as_ref()) {
Ok(j) => j,
Err(e) => {
return SharedString::from(format!("Input is not valid JSON: {e}"));
}
};

if property_name.is_none() && !json.is_object() {
@@ -1363,6 +1436,99 @@ pub fn ui_set_properties(
declarations
}

fn as_json_brush(
kind: BrushKind,
angle: f32,
color: slint::Color,
stops: slint::ModelRc<GradientStop>,
) -> SharedString {
match kind {
BrushKind::Solid => format!("\"{}\"", color_to_string(color)).into(),
BrushKind::Linear => {
let mut result = format!(
"{{\n \"gradient\": \"linear\",\n \"angle\": {angle},\n \"steps\": ["
);
for s in stops.iter() {
result += &format!(
"\n {{ \"color\": {}, \"position\": \"{}\" }}",
color_to_string(s.color),
s.position,
);
}
result += "\n ]\n}";

result.into()
}
BrushKind::Radial => {
let mut result = "{{\n \"gradient\": \"circle\",\n \"steps\": [".to_string();
for s in stops.iter() {
result += &format!(
"\n {{ \"color\": {}, \"position\": \"{}\" }}",
color_to_string(s.color),
s.position,
);
}
result += "\n ]\n}";

result.into()
}
}
}

fn as_slint_brush(
kind: BrushKind,
angle: f32,
color: slint::Color,
stops: slint::ModelRc<GradientStop>,
) -> SharedString {
match kind {
BrushKind::Solid => color_to_string(color).into(),
BrushKind::Linear => {
let mut result = format!("@linear-gradient({angle}deg");
for s in stops.iter() {
result += &format!(", {} {}", color_to_string(s.color), s.position,);
}
result += ")";

result.into()
}
BrushKind::Radial => {
let mut result = "@radial-gradient(circle".to_string();
for s in stops.iter() {
result += &format!(", {} {}", color_to_string(s.color), s.position,);
}
result += ")";

result.into()
}
}
}

fn create_brush(
kind: BrushKind,
angle: f32,
color: slint::Color,
stops: slint::ModelRc<GradientStop>,
) -> slint::Brush {
match kind {
BrushKind::Solid => slint::Brush::SolidColor(color),
BrushKind::Linear => {
slint::Brush::LinearGradient(i_slint_core::graphics::LinearGradientBrush::new(
angle,
stops.iter().map(|gs| i_slint_core::graphics::GradientStop {
position: gs.position,
color: gs.color,
}),
))
}
BrushKind::Radial => slint::Brush::RadialGradient(
i_slint_core::graphics::RadialGradientBrush::new_circle(stops.iter().map(|gs| {
i_slint_core::graphics::GradientStop { position: gs.position, color: gs.color }
})),
),
}
}

#[cfg(test)]
mod tests {
use crate::{language::test::loaded_document_cache, preview::preview_data};
24 changes: 23 additions & 1 deletion tools/lsp/ui/api.slint
Original file line number Diff line number Diff line change
@@ -114,13 +114,26 @@ export enum PropertyValueKind {
string,
}

export enum BrushKind {
solid,
linear,
radial,
}

export struct GradientStop {
position: percent,
color: color,
}

//github.com/ Data about the property value for use in "simple" mode
export struct PropertyValue {
value-bool: bool, // boolean
is-translatable: bool, // string
kind: PropertyValueKind,
brush-kind: BrushKind, // brush
gradient-stops: [GradientStop], // brush
value-brush: brush, // brush, color
value-float: float, // float
value-float: float, // float, brush (angle)
value-int: int, // integer, enum/float (current index into visual_items)
default-selection: int, // enum/float (default index into visual_items)
value-string: string, // enum (name), string, brush (color value)
@@ -478,12 +491,21 @@ export global Api {

pure callback string-to-code(value: string, is_translatable: bool, tr_context: string, tr_plural: string, tr_plural_expression: string) -> string;

pure callback as-slint-brush(kind: BrushKind, angle: float, color: color, stops: [GradientStop]) -> string;

// ## preview data
pure callback get-property-value(component: string, name: string) -> PropertyValue;
pure callback get-property-value-table(component: string, name: string) -> PropertyValueTable;

pure callback set-json-preview-data(component: string, name: string, json-value: string) -> string;

pure callback as-json-brush(kind: BrushKind, angle: float, color: color, stops: [GradientStop]) -> string;

pure callback add-gradient-stop(stops: [GradientStop], row: int, value: GradientStop);
pure callback remove-gradient-stop(stops: [GradientStop], row: int);

pure callback create_brush(kind: BrushKind, angle: float, color: color, stops: [GradientStop]) -> brush;

// Get the property declaration/definition ranges
callback property-declaration-ranges(property-name: string) -> PropertyDeclaration;
}
Loading
Oops, something went wrong.








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/slint-ui/slint/pull/7859/commits/9db858f0281a57aab9b36ac008f158d718b6c86f

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy