openpencil/crates/op-host-native/src/widget_host/property_layout_dispatch.rs
Kayshen-X b0b52a7842
Some checks failed
Rust check (native) / ubuntu-latest / 1.94 (push) Failing after 2s
Rust check (native) / cargo-deny (native) (push) Failing after 1s
Rust check (native) / diagnostics golden drift (push) Failing after 2s
Rust multi-platform build / linux-x86_64 (push) Failing after 1s
Rust multi-platform build / wasm32-unknown-unknown / op-host-web (compile guard) (push) Failing after 2s
Rust multi-platform build / android-aarch64 (cargo check only) (push) Failing after 2s
Rust multi-platform build / android-x86_64 (cargo check only) (push) Failing after 2s
WASM bundle check (kickoff §1.2) / cargo check --target wasm32-unknown-unknown (push) Failing after 2s
WASM bundle check (kickoff §1.2) / cargo-deny --target wasm32-unknown-unknown check bans (push) Failing after 1s
Rust check (native) / macos-latest / 1.94 (push) Has been cancelled
Rust check (native) / windows-latest / 1.94 (push) Has been cancelled
Rust multi-platform build / linux-aarch64 (push) Has been cancelled
Rust multi-platform build / macos-aarch64 (push) Has been cancelled
Rust multi-platform build / windows-x86_64 (push) Has been cancelled
Rust multi-platform build / macos-x86_64 (push) Has been cancelled
Rust multi-platform build / windows-aarch64 (push) Has been cancelled
Rust multi-platform build / ios-aarch64 (cargo check only) (push) Has been cancelled
Rust multi-platform build / ios-aarch64-sim (cargo check only) (push) Has been cancelled
feat(panels): improve native property controls and icons
2026-05-24 23:30:00 +08:00

221 lines
8.8 KiB
Rust

//! Layout and text property dispatch helpers for the native property panel.
use super::WidgetHostNative;
use jian_ops_schema::node::PenNode;
use jian_ops_schema::sizing::{SizingBehavior, SizingKeyword};
use op_editor_core::ui_draft::PropertyFocus;
impl WidgetHostNative {
pub(in crate::widget_host) fn set_selected_layout_mode(
&mut self,
mode: op_editor_core::FlexLayout,
) {
let id = self.editor_state.selection.anchor.clone();
if !id.is_real() {
return;
}
let value = match mode {
op_editor_core::FlexLayout::Free => "none",
op_editor_core::FlexLayout::Vertical => "vertical",
op_editor_core::FlexLayout::Horizontal => "horizontal",
};
self.editor_state.commit_history();
let _ = self
.editor_state
.apply(op_editor_core::EditorCommand::SetNodeLayoutProp {
node_id: id,
property: "layout".to_string(),
value: op_editor_core::LayoutPropValue::Keyword(value.to_string()),
});
}
pub(in crate::widget_host) fn toggle_selected_sizing(
&mut self,
width: bool,
keyword: SizingKeyword,
) {
let id = self.editor_state.selection.anchor.clone();
if !id.is_real() {
return;
}
let (is_current, fallback) = {
let Some(node) = self.editor_state.selected_node() else {
return;
};
let sizing = selected_sizing(node, width);
let is_current = matches!(sizing, Some(SizingBehavior::Keyword(k)) if *k == keyword);
let bounds = op_editor_core::aggregate_bounds(node);
(is_current, if width { bounds.w } else { bounds.h })
};
self.editor_state.commit_history();
if is_current {
let focus = if width {
PropertyFocus::SizeW
} else {
PropertyFocus::SizeH
};
let _ = self
.editor_state
.commit_property_edit(focus, fallback.max(0.0) as f32);
} else {
let prop = if width { "width" } else { "height" };
let value = match keyword {
SizingKeyword::FitContent => "fit_content",
SizingKeyword::FillContainer => "fill_container",
};
let _ = self
.editor_state
.apply(op_editor_core::EditorCommand::SetNodeLayoutProp {
node_id: id,
property: prop.to_string(),
value: op_editor_core::LayoutPropValue::Keyword(value.to_string()),
});
}
}
pub(in crate::widget_host) fn toggle_selected_clip_content(&mut self) {
let id = self.editor_state.selection.anchor.clone();
if !id.is_real() {
return;
}
let current = self
.editor_state
.selected_node()
.map(selected_clip_content)
.unwrap_or(false);
self.editor_state.commit_history();
let _ = self
.editor_state
.apply(op_editor_core::EditorCommand::SetNodeLayoutProp {
node_id: id,
property: "clipContent".to_string(),
value: op_editor_core::LayoutPropValue::Bool(!current),
});
}
pub(in crate::widget_host) fn set_selected_text_align(
&mut self,
value: op_editor_ui::widgets::property_panel::TextAlignValue,
) {
let keyword = match value {
op_editor_ui::widgets::property_panel::TextAlignValue::Left => "left",
op_editor_ui::widgets::property_panel::TextAlignValue::Center => "center",
op_editor_ui::widgets::property_panel::TextAlignValue::Right => "right",
op_editor_ui::widgets::property_panel::TextAlignValue::Justify => "justify",
};
self.set_selected_layout_keyword("textAlign", keyword);
}
pub(in crate::widget_host) fn set_selected_layout_align(
&mut self,
value: op_editor_ui::widgets::property_panel::LayoutAlignValue,
) {
let keyword = match value {
op_editor_ui::widgets::property_panel::LayoutAlignValue::Start => "start",
op_editor_ui::widgets::property_panel::LayoutAlignValue::Center => "center",
op_editor_ui::widgets::property_panel::LayoutAlignValue::End => "end",
};
self.set_selected_layout_keyword("alignItems", keyword);
}
pub(in crate::widget_host) fn set_selected_layout_justify(
&mut self,
value: op_editor_ui::widgets::property_panel::LayoutJustifyValue,
) {
let keyword = match value {
op_editor_ui::widgets::property_panel::LayoutJustifyValue::Start => "start",
op_editor_ui::widgets::property_panel::LayoutJustifyValue::Center => "center",
op_editor_ui::widgets::property_panel::LayoutJustifyValue::End => "end",
op_editor_ui::widgets::property_panel::LayoutJustifyValue::SpaceBetween => {
"space_between"
}
op_editor_ui::widgets::property_panel::LayoutJustifyValue::SpaceAround => {
"space_around"
}
};
self.set_selected_layout_keyword("justifyContent", keyword);
}
pub(in crate::widget_host) fn set_selected_text_vertical_align(
&mut self,
value: op_editor_ui::widgets::property_panel::TextVerticalAlignValue,
) {
let keyword = match value {
op_editor_ui::widgets::property_panel::TextVerticalAlignValue::Top => "top",
op_editor_ui::widgets::property_panel::TextVerticalAlignValue::Middle => "middle",
op_editor_ui::widgets::property_panel::TextVerticalAlignValue::Bottom => "bottom",
};
self.set_selected_layout_keyword("textAlignVertical", keyword);
}
pub(in crate::widget_host) fn set_selected_text_growth(
&mut self,
value: op_editor_ui::widgets::property_panel::TextGrowthValue,
) {
let keyword = match value {
op_editor_ui::widgets::property_panel::TextGrowthValue::Auto => "auto",
op_editor_ui::widgets::property_panel::TextGrowthValue::FixedWidth => "fixed-width",
op_editor_ui::widgets::property_panel::TextGrowthValue::FixedWidthHeight => {
"fixed-width-height"
}
};
self.set_selected_layout_keyword("textGrowth", keyword);
}
pub(in crate::widget_host) fn set_selected_text_font_family(&mut self, family: &str) {
if family.trim().is_empty() {
return;
}
self.set_selected_layout_keyword("fontFamily", family);
}
fn set_selected_layout_keyword(&mut self, property: &str, keyword: &str) {
let id = self.editor_state.selection.anchor.clone();
if !id.is_real() {
return;
}
self.editor_state.commit_history();
let _ = self
.editor_state
.apply(op_editor_core::EditorCommand::SetNodeLayoutProp {
node_id: id,
property: property.to_string(),
value: op_editor_core::LayoutPropValue::Keyword(keyword.to_string()),
});
}
}
fn selected_sizing(node: &PenNode, width: bool) -> Option<&SizingBehavior> {
match (node, width) {
(PenNode::Frame(n), true) => n.container.width.as_ref(),
(PenNode::Frame(n), false) => n.container.height.as_ref(),
(PenNode::Group(n), true) => n.container.width.as_ref(),
(PenNode::Group(n), false) => n.container.height.as_ref(),
(PenNode::Rectangle(n), true) => n.container.width.as_ref(),
(PenNode::Rectangle(n), false) => n.container.height.as_ref(),
(PenNode::Ellipse(n), true) => n.width.as_ref(),
(PenNode::Ellipse(n), false) => n.height.as_ref(),
(PenNode::Polygon(n), true) => n.width.as_ref(),
(PenNode::Polygon(n), false) => n.height.as_ref(),
(PenNode::Path(n), true) => n.width.as_ref(),
(PenNode::Path(n), false) => n.height.as_ref(),
(PenNode::Text(n), true) => n.width.as_ref(),
(PenNode::Text(n), false) => n.height.as_ref(),
(PenNode::TextInput(n), true) => n.width.as_ref(),
(PenNode::TextInput(n), false) => n.height.as_ref(),
(PenNode::Image(n), true) => n.width.as_ref(),
(PenNode::Image(n), false) => n.height.as_ref(),
(PenNode::IconFont(n), true) => n.width.as_ref(),
(PenNode::IconFont(n), false) => n.height.as_ref(),
(PenNode::Line(_) | PenNode::Ref(_), _) => None,
}
}
fn selected_clip_content(node: &PenNode) -> bool {
match node {
PenNode::Frame(n) => n.container.clip_content.unwrap_or(false),
PenNode::Group(n) => n.container.clip_content.unwrap_or(false),
PenNode::Rectangle(n) => n.container.clip_content.unwrap_or(false),
_ => false,
}
}