zng_wgt_undo/
lib.rs
1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12use std::time::Duration;
13
14use zng_ext_undo::*;
15use zng_wgt::prelude::*;
16
17#[property(CONTEXT - 10, default(false))]
25pub fn undo_scope(child: impl UiNode, is_scope: impl IntoVar<bool>) -> impl UiNode {
26 let mut scope = WidgetUndoScope::new();
27 let mut undo_cmd = CommandHandle::dummy();
28 let mut redo_cmd = CommandHandle::dummy();
29 let mut clear_cmd = CommandHandle::dummy();
30 let is_scope = is_scope.into_var();
31 match_node(child, move |c, mut op| {
32 match &mut op {
33 UiNodeOp::Init => {
34 WIDGET.sub_var(&is_scope);
35
36 if !is_scope.get() {
37 return; }
39
40 scope.init();
41
42 let id = WIDGET.id();
43 undo_cmd = UNDO_CMD.scoped(id).subscribe(false);
44 redo_cmd = REDO_CMD.scoped(id).subscribe(false);
45 clear_cmd = CLEAR_HISTORY_CMD.scoped(id).subscribe(false);
46 }
47 UiNodeOp::Deinit => {
48 if !is_scope.get() {
49 return;
50 }
51
52 UNDO.with_scope(&mut scope, || c.deinit());
53 scope.deinit();
54 undo_cmd = CommandHandle::dummy();
55 redo_cmd = CommandHandle::dummy();
56 return;
57 }
58 UiNodeOp::Info { info } => {
59 if !is_scope.get() {
60 return;
61 }
62 scope.info(info);
63 }
64 UiNodeOp::Event { update } => {
65 if !is_scope.get() {
66 return;
67 }
68
69 let id = WIDGET.id();
70 if let Some(args) = UNDO_CMD.scoped(id).on_unhandled(update) {
71 args.propagation().stop();
72 UNDO.with_scope(&mut scope, || {
73 if let Some(&n) = args.param::<u32>() {
74 UNDO.undo_select(n);
75 } else if let Some(&i) = args.param::<Duration>() {
76 UNDO.undo_select(i);
77 } else if let Some(&t) = args.param::<DInstant>() {
78 UNDO.undo_select(t);
79 } else {
80 UNDO.undo();
81 }
82 });
83 } else if let Some(args) = REDO_CMD.scoped(id).on_unhandled(update) {
84 args.propagation().stop();
85 UNDO.with_scope(&mut scope, || {
86 if let Some(&n) = args.param::<u32>() {
87 UNDO.redo_select(n);
88 } else if let Some(&i) = args.param::<Duration>() {
89 UNDO.redo_select(i);
90 } else if let Some(&t) = args.param::<DInstant>() {
91 UNDO.redo_select(t);
92 } else {
93 UNDO.redo();
94 }
95 });
96 } else if let Some(args) = CLEAR_HISTORY_CMD.scoped(id).on_unhandled(update) {
97 args.propagation().stop();
98 UNDO.with_scope(&mut scope, || {
99 UNDO.clear();
100 });
101 }
102 }
103 UiNodeOp::Update { .. } => {
104 if let Some(is_scope) = is_scope.get_new() {
105 WIDGET.info();
106
107 if is_scope {
108 if !scope.is_inited() {
109 scope.init();
110
111 let id = WIDGET.id();
112 undo_cmd = UNDO_CMD.scoped(id).subscribe(false);
113 redo_cmd = REDO_CMD.scoped(id).subscribe(false);
114 }
115 } else if scope.is_inited() {
116 scope.deinit();
117 undo_cmd = CommandHandle::dummy();
118 redo_cmd = CommandHandle::dummy();
119 }
120 }
121 if !is_scope.get() {
122 return;
123 }
124 }
125 _ => {
126 if !is_scope.get() {
127 return;
128 }
129 }
130 }
131
132 UNDO.with_scope(&mut scope, || c.op(op));
133
134 let can_undo = scope.can_undo();
135 let can_redo = scope.can_redo();
136 undo_cmd.set_enabled(can_undo);
137 redo_cmd.set_enabled(can_redo);
138 clear_cmd.set_enabled(can_undo || can_redo);
139 })
140}
141
142#[property(CONTEXT, default(true))]
144pub fn undo_enabled(child: impl UiNode, enabled: impl IntoVar<bool>) -> impl UiNode {
145 let enabled = enabled.into_var();
146 match_node(child, move |c, op| {
147 if !enabled.get() {
148 UNDO.with_disabled(|| c.op(op))
149 }
150 })
151}
152
153#[property(CONTEXT - 11, default(UNDO_LIMIT_VAR))]
159pub fn undo_limit(child: impl UiNode, max: impl IntoVar<u32>) -> impl UiNode {
160 with_context_var(child, UNDO_LIMIT_VAR, max)
161}
162
163#[property(CONTEXT - 11, default(UNDO_INTERVAL_VAR))]
172pub fn undo_interval(child: impl UiNode, interval: impl IntoVar<Duration>) -> impl UiNode {
173 with_context_var(child, UNDO_INTERVAL_VAR, interval)
174}
175
176#[widget_mixin]
186pub struct UndoMix<P>(P);
187
188impl<P: WidgetImpl> UndoMix<P> {
189 fn widget_intrinsic(&mut self) {
190 widget_set! {
191 self;
192 crate::undo_scope = true;
193 }
194 }
195
196 widget_impl! {
197 pub undo_enabled(enabled: impl IntoVar<bool>);
201
202 pub undo_limit(limit: impl IntoVar<u32>);
204
205 pub undo_interval(interval: impl IntoVar<Duration>);
210 }
211}