Skip to content

Commit

Permalink
Added Id to SubClause
Browse files Browse the repository at this point in the history
  • Loading branch information
veeso committed Nov 25, 2021
1 parent de5f24c commit 97c2613
Show file tree
Hide file tree
Showing 4 changed files with 306 additions and 132 deletions.
23 changes: 8 additions & 15 deletions docs/en/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
- [Handle subscriptions](#handle-subscriptions)
- [Event clauses in details](#event-clauses-in-details)
- [Sub clauses in details](#sub-clauses-in-details)
- [Sanitizing the application](#sanitizing-the-application)
- [Subscriptions lock](#subscriptions-lock)
- [Tick Event](#tick-event)
- [Ports](#ports)
- [Implementing new components](#implementing-new-components)
Expand Down Expand Up @@ -152,8 +152,9 @@ Sub clauses are verified once the event clause is satisfied, and they define som
In particular sub clauses are:

- `Always`: the clause is always satisfied
- `HasAttrValue(Attribute, AttrValue)`: the clause is satisfied if the target component has `Attribute` with `AttrValue` in its `Props`.
- `HasState(State)`: the clause is satisfied if the target component has `State` equal to provided state.
- `HasAttrValue(Id, Attribute, AttrValue)`: the clause is satisfied if the target component (defined in `Id`) has `Attribute` with `AttrValue` in its `Props`.
- `HasState(Id, State)`: the clause is satisfied if the target component (defined in `Id`) has `State` equal to provided state.
- `IsMounted(Id)`: the clause is satisfied if the target component (defines in `Id`) is mounted in the View.

In addition to these, it is also possible to combine Sub clauses using expressions:

Expand All @@ -165,20 +166,12 @@ Using `And` and `Or` you can create even long expression and keep in mind that t

`And(Or(A, And(B, C)), And(D, Or(E, F)))` is evaluated as `(A || (B && C)) && (D && (E || F))`

### Sanitizing the application
### Subscriptions lock

First of all let's say one thing:
It is possible to temporarily disable the subscriptions propagation.
To do so, you just need to call `application.lock_subs()`.

- The subscriptions are handled by the **Application**, **not** by the **View**.
- Whenever you *umount* a component from the **Application** all subscriptions associated to it will be removed.

Said so, sanitizing the application means to remove all orphan subscriptions, which means removing all the subscriptions associated to a component which is no longer mounted in the view.

But if application always removes subscriptions when umount, how can this possibly happen? 馃

Well, there is a way to achieve this. If you umount a component from the **View** in the **Update routine**, subscriptions won't be checked.
Whenever you umount components that have subscriptions, you should call `application.sanitize()` after the update
routine is finished.
Whenever you want to restore event propagation, just call `application.unlock_subs()`.

---

Expand Down
72 changes: 51 additions & 21 deletions src/core/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ where
&mut self,
id: K,
component: WrappedComponent<Msg, UserEvent>,
subs: Vec<Sub<UserEvent>>,
subs: Vec<Sub<K, UserEvent>>,
) -> ApplicationResult<()> {
// Mount
self.view.mount(id.clone(), component)?;
Expand Down Expand Up @@ -167,7 +167,7 @@ where
&mut self,
id: K,
component: WrappedComponent<Msg, UserEvent>,
subs: Vec<Sub<UserEvent>>,
subs: Vec<Sub<K, UserEvent>>,
) -> ApplicationResult<()> {
let had_focus = self.view.has_focus(&id);
let _ = self.umount(&id);
Expand Down Expand Up @@ -259,7 +259,7 @@ where
///
/// Subscribe component to a certain event.
/// Returns Error if the component doesn't exist or if the component is already subscribed to this event
pub fn subscribe(&mut self, id: &K, sub: Sub<UserEvent>) -> ApplicationResult<()> {
pub fn subscribe(&mut self, id: &K, sub: Sub<K, UserEvent>) -> ApplicationResult<()> {
if !self.view.mounted(id) {
return Err(ViewError::ComponentNotFound.into());
}
Expand Down Expand Up @@ -395,8 +395,9 @@ where
}
if !sub.forward(
ev,
|q| self.view.component(sub.target()).unwrap().query(q),
|| self.view.component(sub.target()).unwrap().state(),
|id, q| self.view.query(id, q).ok().flatten(),
|id| self.view.state(id).ok(),
|id| self.view.mounted(id),
) {
continue;
}
Expand Down Expand Up @@ -571,11 +572,19 @@ mod test {
Sub::new(SubEventClause::Tick, SubClause::Always),
Sub::new(
SubEventClause::Tick,
SubClause::HasAttrValue(Attribute::InputLength, AttrValue::Length(8))
SubClause::HasAttrValue(
MockComponentId::InputFoo,
Attribute::InputLength,
AttrValue::Length(8)
)
), // NOTE: This event will be ignored
Sub::new(
SubEventClause::User(MockEvent::Bar),
SubClause::HasAttrValue(Attribute::Focus, AttrValue::Flag(true))
SubClause::HasAttrValue(
MockComponentId::InputFoo,
Attribute::Focus,
AttrValue::Flag(true)
)
)
]
)
Expand All @@ -587,7 +596,11 @@ mod test {
&MockComponentId::InputFoo,
Sub::new(
SubEventClause::User(MockEvent::Foo),
SubClause::HasAttrValue(Attribute::Focus, AttrValue::Flag(false))
SubClause::HasAttrValue(
MockComponentId::InputFoo,
Attribute::Focus,
AttrValue::Flag(false)
)
)
)
.is_ok());
Expand All @@ -598,7 +611,11 @@ mod test {
&MockComponentId::InputFoo,
Sub::new(
SubEventClause::User(MockEvent::Foo),
SubClause::HasAttrValue(Attribute::Focus, AttrValue::Flag(false))
SubClause::HasAttrValue(
MockComponentId::InputFoo,
Attribute::Focus,
AttrValue::Flag(false)
)
)
)
.is_err());
Expand All @@ -608,7 +625,11 @@ mod test {
&MockComponentId::InputBar,
Sub::new(
SubEventClause::User(MockEvent::Foo),
SubClause::HasAttrValue(Attribute::Focus, AttrValue::Flag(false))
SubClause::HasAttrValue(
MockComponentId::InputBar,
Attribute::Focus,
AttrValue::Flag(false)
)
)
)
.is_err());
Expand Down Expand Up @@ -640,7 +661,11 @@ mod test {
Sub::new(SubEventClause::Tick, SubClause::Always),
Sub::new(
SubEventClause::User(MockEvent::Bar),
SubClause::HasAttrValue(Attribute::Focus, AttrValue::Flag(true))
SubClause::HasAttrValue(
MockComponentId::InputFoo,
Attribute::Focus,
AttrValue::Flag(true)
)
)
]
)
Expand Down Expand Up @@ -681,7 +706,11 @@ mod test {
Sub::new(
// NOTE: won't be thrown, since requires focus
SubEventClause::Keyboard(KeyEvent::from(Key::Enter)),
SubClause::HasAttrValue(Attribute::Focus, AttrValue::Flag(true))
SubClause::HasAttrValue(
MockComponentId::InputBar,
Attribute::Focus,
AttrValue::Flag(true)
)
)
]
)
Expand Down Expand Up @@ -721,14 +750,11 @@ mod test {
&[MockMsg::BarSubmit(String::from(""))]
);
// Let's try TryFor strategy
assert!(
application
.tick(PollStrategy::TryFor(Duration::from_millis(300)))
.ok()
.unwrap()
.len()
>= 3
);
let events = application
.tick(PollStrategy::TryFor(Duration::from_millis(300)))
.ok()
.unwrap();
assert!(events.len() >= 2);
}

#[test]
Expand All @@ -752,7 +778,11 @@ mod test {
Sub::new(
// NOTE: won't be thrown, since requires focus
SubEventClause::Keyboard(KeyEvent::from(Key::Enter)),
SubClause::HasAttrValue(Attribute::Focus, AttrValue::Flag(true))
SubClause::HasAttrValue(
MockComponentId::InputBar,
Attribute::Focus,
AttrValue::Flag(true)
)
)
]
)
Expand Down

0 comments on commit 97c2613

Please sign in to comment.