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

Repeat/Conditional editing #282

Merged
merged 3 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions pax-chassis-web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,12 +623,34 @@ impl PaxChassisWeb {
engine.full_reload_userland(root);
}
ReloadType::Partial(uni) => {
let instance_node = self
let manifest = self
.userland_definition_to_instance_traverser
.build_template_node(
&uni.get_containing_component_type_id(),
&uni.get_template_node_id(),
);
.get_manifest();
let containing_component = manifest
.components
.get(&uni.get_containing_component_type_id())
.unwrap();
let containing_template = containing_component.template.as_ref().unwrap();
let tnd = containing_template
.get_node(&uni.get_template_node_id())
.unwrap();
let pax_type = tnd.type_id.get_pax_type();
let instance_node = match pax_type {
pax_manifest::PaxType::If
| pax_manifest::PaxType::Slot
| pax_manifest::PaxType::Repeat => self
.userland_definition_to_instance_traverser
.build_control_flow(
&uni.get_containing_component_type_id(),
&uni.get_template_node_id(),
),
_ => self
.userland_definition_to_instance_traverser
.build_template_node(
&uni.get_containing_component_type_id(),
&uni.get_template_node_id(),
),
};
let mut engine = borrow_mut!(self.engine);
engine.partial_update_expanded_node(Rc::clone(&instance_node));
}
Expand Down
8 changes: 7 additions & 1 deletion pax-designer/src/context_menu/mod.pax
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
if self.visible {
<Group x={(self.pos_x)px} y={(self.pos_y)px} width=150px height=206px>
<Group x={(self.pos_x)px} y={(self.pos_y)px} width=150px height=240px>
<Stacker width={100% - 10px} height={100% - 10px} x=50% y=50% direction=StackerDirection::Vertical >
<Group height=28px @click=self.group>
<Text height=100% text="Group" id=text x=5px/>
Expand All @@ -10,6 +10,12 @@ if self.visible {
<Group height=28px @click=self.group_link>
<Text height=100% text="Make Link" id=text x=5px/>
</Group>
<Group height=28px @click=self.group_repeat>
<Text height=100% text="Repeat" id=text x=5px/>
</Group>
<Group height=28px @click=self.group_conditional>
<Text height=100% text="Make Conditional" id=text x=5px/>
</Group>
<Group height=28px @click=self.move_top>
<Text height=100% text="Move to Top" id=text x=5px/>
</Group>
Expand Down
20 changes: 20 additions & 0 deletions pax-designer/src/context_menu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,26 @@ impl DesignerContextMenu {
self.close_menu();
}

pub fn group_conditional(&mut self, ctx: &NodeContext, _args: Event<Click>) {
model::perform_action(
&GroupSelected {
group_type: GroupType::Conditional,
},
ctx,
);
self.close_menu();
}

pub fn group_repeat(&mut self, ctx: &NodeContext, _args: Event<Click>) {
model::perform_action(
&GroupSelected {
group_type: GroupType::Repeat,
},
ctx,
);
self.close_menu();
}

pub fn ungroup(&mut self, ctx: &NodeContext, _args: Event<Click>) {
model::perform_action(&UngroupSelected {}, ctx);
self.close_menu();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ impl ToolBehavior for DropComponent {
let uid = CreateComponent {
parent_id: &parent.id,
parent_index: pax_manifest::TreeIndexPosition::Top,
node_layout: model::action::orm::NodeLayoutSettings::KeepScreenBounds {
node_layout: Some(model::action::orm::NodeLayoutSettings::KeepScreenBounds {
node_transform_and_bounds: &TransformAndBounds {
transform: bounds.as_transform(),
bounds: (1.0, 1.0),
}
.as_pure_size(),
parent_transform_and_bounds: &parent.transform_and_bounds.get(),
node_decomposition_config: &Default::default(),
},
}),
builder_extra_commands: None,
designer_node_type: DesignerNodeType::from_type_id(self.type_id.clone()),
}
Expand Down
61 changes: 61 additions & 0 deletions pax-designer/src/controls/settings/control_flow_for_editor.pax
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@

<Group width=90% x=50%>
<Text y=20px text="Predicate" class=h2/>
<Textbox y=50px class=input text=bind:self.for_predicate @textbox_change=self.commit_predicate/>
<Text y=90px text="Source" class=h2/>
<Textbox y=120px class=input text=bind:self.for_source @textbox_change=self.commit_source/>
</Group>

@settings {
@mount: on_mount
#text {
selectable: false,
style: {
font: {Font::Web(
"ff-real-headline-pro",
"https://use.typekit.net/ivu7epf.css",
FontStyle::Normal,
FontWeight::Bold,
)},
font_size: 10px,
fill: {rgba(255, 0.3*255, 0.3*255, 255)},
align_vertical: TextAlignVertical::Top,
align_horizontal: TextAlignHorizontal::Left,
align_multiline: TextAlignHorizontal::Left
}
}

.input {
height: 30px,
background: rgb(12.5%, 12.5%, 12.5%),
stroke: {
color: rgb(48, 56, 62),
width: 1px,
},
border_radius: 5,
style: {
font: {Font::Web(
"ff-real-headline-pro",
"https://use.typekit.net/ivu7epf.css",
FontStyle::Normal,
FontWeight::ExtraLight,
)},
font_size: 13px,
fill: WHITE,
}
}

.h2 {
height: 20px,
style: {
font: {Font::Web(
"ff-real-headline-pro",
"https://use.typekit.net/ivu7epf.css",
FontStyle::Normal,
FontWeight::Light
)},
font_size: 16px,
fill: WHITE,
}
}
}
100 changes: 100 additions & 0 deletions pax-designer/src/controls/settings/control_flow_for_editor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use anyhow::anyhow;
use pax_engine::api::*;
use pax_engine::pax_manifest::{TemplateNodeId, TypeId, UniqueTemplateNodeIdentifier};
use pax_engine::*;
use pax_std::*;

use crate::model;

#[pax]
#[engine_import_path("pax_engine")]
#[file("controls/settings/control_flow_for_editor.pax")]
pub struct ControlFlowForEditor {
pub stid: Property<TypeId>,
pub snid: Property<TemplateNodeId>,
pub for_source: Property<String>,
pub for_predicate: Property<String>,
}

impl ControlFlowForEditor {
pub fn on_mount(&mut self, ctx: &NodeContext) {
// TODO hook up for source/predicate to reactively be read on manifest change
let dt = borrow!(ctx.designtime);
let manifest_ver = dt.get_manifest_version();
let deps = [manifest_ver.untyped()];
let ctx = ctx.clone();
let stid = self.stid.clone();
let snid = self.snid.clone();
let control_flow_parts = Property::computed(
move || {
let mut dt = borrow_mut!(ctx.designtime);
let uni = UniqueTemplateNodeIdentifier::build(stid.get(), snid.get());
if let Some(control_flow_data) = dt
.get_orm_mut()
.get_node(uni, false)
.and_then(|mut n| n.get_control_flow_properties())
{
(
control_flow_data
.repeat_predicate_definition
.map(|v| match v {
pax_manifest::ControlFlowRepeatPredicateDefinition::ElemId(ind) => ind.to_string(),
pax_manifest::ControlFlowRepeatPredicateDefinition::ElemIdIndexId(elem, ind) => format!("({},{})", elem, ind),
}).unwrap_or_else(|| "<error>".to_string()),
control_flow_data
.repeat_source_expression
.map(|v| v.to_string()).unwrap_or_else(|| "<error>".to_string()),
)
} else {
log::warn!("couldn't fetch if definition");
("<error>".to_string(), "<error>".to_string())
}
},
&deps,
);
let deps = [control_flow_parts.untyped()];
let control_flow_parts_cp = control_flow_parts.clone();
self.for_source.replace_with(Property::computed(
move || control_flow_parts_cp.get().1,
&deps,
));
self.for_predicate.replace_with(Property::computed(
move || control_flow_parts.get().0,
&deps,
));
}

pub fn commit_source(&mut self, ctx: &NodeContext, event: Event<TextboxChange>) {
let t = model::with_action_context(ctx, |ac| ac.transaction("writing if expression"));
let _ = t.run(|| {
let uid = UniqueTemplateNodeIdentifier::build(self.stid.get(), self.snid.get());
let mut dt = borrow_mut!(ctx.designtime);
let orm = dt.get_orm_mut();
let mut node = orm
.get_node(uid, true)
.ok_or_else(|| anyhow!("failed to get node"))?;
node.set_repeat_source(&format!("{{{}}}", event.text))
.map_err(|e| anyhow!("failed to set repeat source: {e}"))?;
node.save()
.map_err(|e| anyhow!("failed to save repeat source: {e}"))?;
Ok(())
});
}

pub fn commit_predicate(&mut self, ctx: &NodeContext, event: Event<TextboxChange>) {
let t = model::with_action_context(ctx, |ac| ac.transaction("writing if expression"));
let _ = t.run(|| {
let uid = UniqueTemplateNodeIdentifier::build(self.stid.get(), self.snid.get());
let mut dt = borrow_mut!(ctx.designtime);
let orm = dt.get_orm_mut();
let mut node = orm
.get_node(uid, true)
.ok_or_else(|| anyhow!("failed to get node"))?;
node.set_repeat_predicate(&format!("{}", event.text))
.map_err(|e| anyhow!("failed to set repeat predicate: {e}"))?;
node.save()
.map_err(|e| anyhow!("failed to save repeat predicate: {e}"))?;
Ok(())
});
}
}
57 changes: 57 additions & 0 deletions pax-designer/src/controls/settings/control_flow_if_editor.pax
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<Group width=90% x=50%>
<Text y=20px text="Source" class=h2/>
<Textbox y=50px class=input text=bind:self.if_source @textbox_change=self.commit/>
</Group>

@settings {
@mount: on_mount
#text {
selectable: false,
style: {
font: {Font::Web(
"ff-real-headline-pro",
"https://use.typekit.net/ivu7epf.css",
FontStyle::Normal,
FontWeight::Bold,
)},
font_size: 10px,
fill: {rgba(255, 0.3*255, 0.3*255, 255)},
align_vertical: TextAlignVertical::Top,
align_horizontal: TextAlignHorizontal::Left,
align_multiline: TextAlignHorizontal::Left
}
}

.input {
height: 30px,
background: rgb(12.5%, 12.5%, 12.5%),
stroke: {
color: rgb(48, 56, 62),
width: 1px,
},
border_radius: 5,
style: {
font: {Font::Web(
"ff-real-headline-pro",
"https://use.typekit.net/ivu7epf.css",
FontStyle::Normal,
FontWeight::ExtraLight,
)},
font_size: 13px,
fill: WHITE,
}
}
.h2 {
height: 20px,
style: {
font: {Font::Web(
"ff-real-headline-pro",
"https://use.typekit.net/ivu7epf.css",
FontStyle::Normal,
FontWeight::Light
)},
font_size: 16px,
fill: WHITE,
}
}
}
63 changes: 63 additions & 0 deletions pax-designer/src/controls/settings/control_flow_if_editor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use anyhow::anyhow;
use pax_engine::api::*;
use pax_engine::pax_manifest::{TemplateNodeId, TypeId, UniqueTemplateNodeIdentifier};
use pax_engine::*;
use pax_std::*;

use crate::model;

#[pax]
#[engine_import_path("pax_engine")]
#[file("controls/settings/control_flow_if_editor.pax")]
pub struct ControlFlowIfEditor {
pub stid: Property<TypeId>,
pub snid: Property<TemplateNodeId>,
pub if_source: Property<String>,
}

impl ControlFlowIfEditor {
pub fn on_mount(&mut self, ctx: &NodeContext) {
// TODO hook up if_source to reactively be read on manifest change
let dt = borrow!(ctx.designtime);
let manifest_ver = dt.get_manifest_version();
let deps = [manifest_ver.untyped()];
let ctx = ctx.clone();
let stid = self.stid.clone();
let snid = self.snid.clone();
self.if_source.replace_with(Property::computed(
move || {
let mut dt = borrow_mut!(ctx.designtime);
let uni = UniqueTemplateNodeIdentifier::build(stid.get(), snid.get());
if let Some(conditional_expression) = dt
.get_orm_mut()
.get_node(uni, false)
.and_then(|mut n| n.get_control_flow_properties())
.and_then(|cf| cf.condition_expression)
{
conditional_expression.to_string()
} else {
log::warn!("couldn't fetch if definition");
"<error>".to_string()
}
},
&deps,
));
}

pub fn commit(&mut self, ctx: &NodeContext, event: Event<TextboxChange>) {
let t = model::with_action_context(ctx, |ac| ac.transaction("writing if expression"));
let _ = t.run(|| {
let uid = UniqueTemplateNodeIdentifier::build(self.stid.get(), self.snid.get());
let mut dt = borrow_mut!(ctx.designtime);
let orm = dt.get_orm_mut();
let mut node = orm
.get_node(uid, true)
.ok_or_else(|| anyhow!("failed to get node"))?;
node.set_conditional_source(&format!("{{{}}}", event.text))
.map_err(|e| anyhow!("failed to set conditional source: {e}"))?;
node.save()
.map_err(|e| anyhow!("failed to save conditional source: {e}"))?;
Ok(())
});
}
}
Loading
Loading