Skip to content

Commit

Permalink
Close #2 - add signals_inhibit
Browse files Browse the repository at this point in the history
  • Loading branch information
idanarye committed Feb 3, 2021
1 parent 08ea89b commit 79abc12
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 40 deletions.
72 changes: 57 additions & 15 deletions examples/example_events.glade
Original file line number Diff line number Diff line change
@@ -1,46 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.36.0 -->
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.22"/>
<object class="GtkTextBuffer" id="buf_count_pressed_time"/>
<object class="GtkApplicationWindow" id="win_app">
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<!-- n-columns=2 n-rows=3 -->
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Conut Pressed Time</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<signal name="button-press-event" handler="Press" swapped="no"/>
<signal name="button-release-event" handler="Release" swapped="no"/>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkTextView">
<property name="width_request">100</property>
<property name="height_request">20</property>
<property name="width-request">100</property>
<property name="height-request">20</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can-focus">True</property>
<property name="editable">False</property>
<property name="buffer">buf_count_pressed_time</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">All Characters</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Only The Digits</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkEntry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<signal name="key-press-event" handler="AllCharactersEntryKeyPressed" swapped="no"/>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="only_digits">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="editable">False</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
</object>
</child>
<child type="titlebar">
<placeholder/>
</child>
</object>
</interface>
32 changes: 26 additions & 6 deletions examples/example_events.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::time::{Instant, Duration};

use gtk::prelude::*;

#[derive(woab::Factories)]
pub struct Factories {
#[factory(extra(buf_count_pressed_time))]
Expand All @@ -10,6 +12,7 @@ pub struct Factories {
pub struct WindowWidgets {
win_app: gtk::ApplicationWindow,
buf_count_pressed_time: gtk::TextBuffer,
only_digits: gtk::Entry,
}

struct WindowActor {
Expand All @@ -22,7 +25,6 @@ impl actix::Actor for WindowActor {
type Context = actix::Context<Self>;

fn started(&mut self, _ctx: &mut Self::Context) {
use gtk::WidgetExt;
self.update_pressed_time_display();
self.widgets.win_app.show();
}
Expand All @@ -34,7 +36,6 @@ impl actix::Actor for WindowActor {

impl WindowActor {
fn update_pressed_time_display(&self) {
use gtk::prelude::*;
self.widgets.buf_count_pressed_time.set_text(&format!(
"L: {:?}, R: {:?}",
self.total_durations[0],
Expand All @@ -43,12 +44,13 @@ impl WindowActor {
}
}

#[derive(woab::BuilderSignal)]
#[derive(Debug, woab::BuilderSignal)]
enum WindowSignal {
#[signal(ret = false)]
#[signal(inhibit = false)]
Press(gtk::Button, #[signal(event)] gdk::EventButton),
#[signal(ret = false)]
#[signal(inhibit = false)]
Release(gtk::Button, #[signal(event)] gdk::EventButton),
AllCharactersEntryKeyPressed(gtk::Entry, #[signal(event)] gdk::EventKey),
}

impl actix::StreamHandler<WindowSignal> for WindowActor {
Expand Down Expand Up @@ -78,6 +80,15 @@ impl actix::StreamHandler<WindowSignal> for WindowActor {
self.update_pressed_time_display();
}
}
WindowSignal::AllCharactersEntryKeyPressed(_, event) => {
if let Some(character) = event.get_keyval().to_unicode() {
if character.is_digit(10) {
let mut text = self.widgets.only_digits.get_text().as_str().to_owned();
text.push(character);
self.widgets.only_digits.set_text(&text);
}
}
}
}
}
}
Expand All @@ -88,7 +99,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
gtk::init()?;
woab::run_actix_inside_gtk_event_loop("example")?;

factories.win_app.build().actor(|_, widgets| WindowActor {
factories.win_app.build().signals_inhibit(|signal| {
match signal {
WindowSignal::AllCharactersEntryKeyPressed(_, event) => {
let character = event.get_keyval().to_unicode();
let is_digit = character.map(|c| c.is_digit(10)).unwrap_or(false);
Some(gtk::Inhibit(is_digit))
}
_ => None,
}
}).actor(|_, widgets| WindowActor {
widgets,
press_times: Default::default(),
total_durations: Default::default(),
Expand Down
36 changes: 24 additions & 12 deletions macros/src/builder_signal_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,38 @@ pub fn impl_builder_signal_derive(ast: &syn::DeriveInput) -> Result<proc_macro2:
};
let enum_ident = &ast.ident;
let match_arms = data_enum.variants.iter().map(|variant| {
let mut ret = None;
let mut inhibit = None;
iter_attrs_parameters(&variant.attrs, "signal", |name, value| {
match path_to_single_string(&name)?.as_str() {
"ret" => {
let value = value.ok_or_else(|| Error::new_spanned(name, "`ret` must have a value"))?;
if ret.is_some() {
return Err(Error::new_spanned(value, "`ret` already set"));
"inhibit" => {
let value = value.ok_or_else(|| Error::new_spanned(name, "`inhibit` must have a value"))?;
if inhibit.is_some() {
return Err(Error::new_spanned(value, "`inhibit` already set"));
}
ret = Some(value);
inhibit = Some(value);
}
_ => {
return Err(Error::new_spanned(name, "unknown argument"));
}
}
Ok(())
})?;
let signal_return_value = if let Some(ret) = ret {
let signal_return_value = if let Some(inhibit) = inhibit {
quote! {
Some(glib::value::ToValue::to_value(&#ret))
Some(glib::value::ToValue::to_value(&#inhibit))
}
} else {
quote!(None)
quote! {
if let Some(inhibit_dlg) = &inhibit_dlg {
if let Some(gtk::Inhibit(inhibit)) = inhibit_dlg(&signal) {
Some(glib::value::ToValue::to_value(&inhibit))
} else {
None
}
} else {
None
}
}
};

let variant_ident = &variant.ident;
Expand Down Expand Up @@ -94,8 +104,10 @@ pub fn impl_builder_signal_derive(ast: &syn::DeriveInput) -> Result<proc_macro2:
};
Ok(quote! {
#ident_as_str => Some(Box::new(move |args| {
match tx.clone().try_send(#msg_construction) {
Ok(_) => #signal_return_value,
let signal = #msg_construction;
let return_value = #signal_return_value;
match tx.clone().try_send(signal) {
Ok(_) => return_value,
Err(tokio::sync::mpsc::error::TrySendError::Closed(_)) => {
panic!("Unable to send {} signal - channel is closed", #ident_as_str);
},
Expand All @@ -108,7 +120,7 @@ pub fn impl_builder_signal_derive(ast: &syn::DeriveInput) -> Result<proc_macro2:
}).collect::<Result<Vec<_>, Error>>()?;
Ok(quote! {
impl woab::BuilderSignal for #enum_ident {
fn transmit_signal_in_stream_function(signal: &str, tx: tokio::sync::mpsc::Sender<Self>) -> Option<Box<dyn Fn(&[glib::Value]) -> Option<glib::Value>>> {
fn transmit_signal_in_stream_function(signal: &str, tx: tokio::sync::mpsc::Sender<Self>, inhibit_dlg: Option<std::rc::Rc<dyn Fn(&Self) -> Option<gtk::Inhibit>>>) -> Option<Box<dyn Fn(&[glib::Value]) -> Option<glib::Value>>> {
use tokio::sync::mpsc::error::TrySendError;
match signal {
#(#match_arms)*
Expand Down
12 changes: 7 additions & 5 deletions src/builder_signal.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::rc::Rc;

use tokio::sync::mpsc;

/// Represent a GTK signal that originates from a GTK builder. Refer to [the corresponding derive](derive.BuilderSignal.html).
Expand All @@ -6,18 +8,18 @@ pub trait BuilderSignal: Sized + 'static {
///
/// The returned function should convert the signals it revceives to the signal type, and
/// transmit them over `tx`.
fn transmit_signal_in_stream_function(signal: &str, tx: mpsc::Sender<Self>) -> Option<Box<dyn Fn(&[glib::Value]) -> Option<glib::Value>>>;
fn transmit_signal_in_stream_function(signal: &str, tx: mpsc::Sender<Self>, inhibit_dlg: Option<Rc<dyn Fn(&Self) -> Option<gtk::Inhibit>>>) -> Option<Box<dyn Fn(&[glib::Value]) -> Option<glib::Value>>>;

/// Create a stream of all the signals.
///
/// Will return `None` if there are no signals, to allow avoiding closed streams.
fn stream_builder_signals(builder: &gtk::Builder) -> Option<mpsc::Receiver<Self>> {
fn stream_builder_signals(builder: &gtk::Builder, inhibit_dlg: Option<Rc<dyn Fn(&Self) -> Option<gtk::Inhibit>>>) -> Option<mpsc::Receiver<Self>> {
use gtk::prelude::BuilderExtManual;

let (tx, rx) = mpsc::channel(16);
let mut connected_any = false;
builder.connect_signals(|_, signal| {
if let Some(handler) = Self::transmit_signal_in_stream_function(signal, tx.clone()) {
if let Some(handler) = Self::transmit_signal_in_stream_function(signal, tx.clone(), inhibit_dlg.clone()) {
connected_any = true;
handler
} else {
Expand All @@ -32,10 +34,10 @@ pub trait BuilderSignal: Sized + 'static {
}

/// Connect the signals created from a GTK builder to an actor's context.
fn connect_builder_signals<H: actix::StreamHandler<Self>>(ctx: &mut H::Context, builder: &gtk::Builder)
fn connect_builder_signals<H: actix::StreamHandler<Self>>(ctx: &mut H::Context, builder: &gtk::Builder, inhibit_dlg: Option<Rc<dyn Fn(&Self) -> Option<gtk::Inhibit>>>)
where <H as actix::Actor>::Context: actix::AsyncContext<H>
{
if let Some(rx) = Self::stream_builder_signals(builder) {
if let Some(rx) = Self::stream_builder_signals(builder, inhibit_dlg) {
H::add_stream(rx, ctx);
}
}
Expand Down
12 changes: 10 additions & 2 deletions src/factories.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::convert::TryInto;
use std::rc::Rc;

use gtk::Builder;
use tokio::sync::mpsc;
Expand Down Expand Up @@ -156,13 +157,15 @@ impl<A, W, S> Factory<A, W, S> {
/// See [`woab::Factory`](struct.Factory.html) for usage example.
pub struct BuilderUtilizer<A, W, S> {
builder: gtk::Builder,
inhibit_dlg: Option<Rc<dyn Fn(&S) -> Option<gtk::Inhibit>>>,
_phantom: std::marker::PhantomData<(A, W, S)>,
}

impl<A, W, S> From<gtk::Builder> for BuilderUtilizer<A, W, S> {
fn from(builder: gtk::Builder) -> Self {
Self {
builder,
inhibit_dlg: None,
_phantom: Default::default(),
}
}
Expand Down Expand Up @@ -195,7 +198,7 @@ where
pub fn actor(&self, make_actor: impl FnOnce(&mut A::Context, W) -> A) -> Result<actix::Addr<A>, <&gtk::Builder as TryInto<W>>::Error> {
let widgets: W = self.widgets()?;
Ok(<A as actix::Actor>::create(move |ctx| {
S::connect_builder_signals::<A>(ctx, &self.builder);
S::connect_builder_signals::<A>(ctx, &self.builder, self.inhibit_dlg.clone());
make_actor(ctx, widgets)
}))
}
Expand All @@ -205,6 +208,11 @@ impl<A, W, S> BuilderUtilizer<A, W, S>
where
S: BuilderSignal,
{
pub fn signals_inhibit(&mut self, inhibit_dlg: impl 'static + Fn(&S) -> Option<gtk::Inhibit>) -> &mut Self {
self.inhibit_dlg = Some(Rc::new(inhibit_dlg));
self
}

/// Create a stream (based on Tokio's MSPC) of signals that arrive from the builder.
///
/// * The signals are all represented by the third generic parameter (`S`) of
Expand All @@ -215,7 +223,7 @@ where
/// returned a stream stream will be closed automatically for having no transmitters, which -
/// by default - will make Actix close the actor.
pub fn stream_builder_signals(&self) -> Option<mpsc::Receiver<S>> {
S::stream_builder_signals(&self.builder)
S::stream_builder_signals(&self.builder, self.inhibit_dlg.clone())
}

/// Stream the signals generated by the builder to an actor represented by `ctx`, together with
Expand Down

0 comments on commit 79abc12

Please sign in to comment.