1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
use core::convert::TryInto; use gtk::Builder; use tokio::sync::mpsc; use tokio::stream::StreamExt; use crate::BuilderSignal; /// Holds instructions for generating a GTK builder. /// /// ```no_run /// # use gtk::prelude::*; /// # use woab::BuilderFactory; /// let builder_xml = r#" /// <interface> /// <requires lib="gtk+" version="3.22"/> /// <object class="GtkButton" id="my_button"> /// ... /// </object> /// </interface> /// "#; /// let builder_factory: BuilderFactory = builder_xml.to_owned().into(); /// let builder = builder_factory.build(); /// let my_button: gtk::Button = builder.get_object("my_button").unwrap(); /// ``` /// /// Refer to [`#[derive(woab::Factories)]`](derive.Factories.html) for how to create instances of /// this struct. pub struct BuilderFactory(String); impl From<String> for BuilderFactory { fn from(xml: String) -> Self { Self(xml) } } impl BuilderFactory { /// Create a `gtk::Builder` from the instructions inside this factory. /// /// Note that "creating a builder" means that the GTK widgets are created (but not yet shown) pub fn build(&self) -> Builder { Builder::from_string(&self.0) } } /// Holds instructions for generating GTK widgets and connecing them to Actix actors. /// /// 1. The first generic parameter, `A`, is the actor type. /// 2. The second generic parameter, `W`, is the widgets type. Typically created with /// [`#[derive(woab::WidgetsFromBuilder)]`](derive.WidgetsFromBuilder.html) on a struct that /// specifies the widgets of the Glade XML file that the code needs to access. /// 3. The third generic parameter, `S`, is the signal type. Typically created with /// [`#[derive(woab::BuilderSignal)]`](derive.BuilderSignal.html) on an enum that lists the /// signals from the Glade XML file that the code wants to handle. /// /// `A` can be `()` if the widgets are to be handled by an existing actor - usually the one that /// handles their parent widget. `S` can also be `()` if it is desired to just generate widgets /// without connecting a signal. /// /// Refer to [`#[derive(woab::Factories)]`](derive.Factories.html) for how to create instances of /// this struct. /// /// ```no_run /// # use gtk::prelude::*; /// #[derive(woab::Factories)] /// struct Factories { /// window: woab::Factory<WindowActor, WindowWidgets, WindowSignal>, /// row: woab::Factory<(), RowWidgets, RowSignal>, /// } /// /// struct WindowActor { /// widgets: WindowWidgets, /// } /// # impl actix::Actor for WindowActor { /// # type Context = actix::Context<Self>; /// # } /// # #[derive(woab::BuilderSignal)] /// # enum WindowSignal {} /// /// impl actix::StreamHandler<WindowSignal> for WindowActor { /// fn handle(&mut self, signal: WindowSignal, _ctx: &mut <Self as actix::Actor>::Context) { /// match signal { /// // Handle the signals of the main window /// } /// } /// } /// /// #[derive(woab::WidgetsFromBuilder)] /// struct WindowWidgets { /// window: gtk::ApplicationWindow, /// list_box: gtk::ListBox, /// } /// /// #[derive(woab::WidgetsFromBuilder)] /// struct RowWidgets { /// row: gtk::ListBoxRow, /// label: gtk::Label, /// } /// # #[derive(woab::BuilderSignal)] /// # enum RowSignal {} /// /// impl actix::StreamHandler<(usize, RowSignal)> for WindowActor { /// fn handle(&mut self, (row_number, signal): (usize, RowSignal), _ctx: &mut <Self as actix::Actor>::Context) { /// match signal { /// // Handle the signals of row #row_number /// } /// } /// /// // ****************************************************** /// // * VERY IMPORTANT! Otherwise once one row is deleted, * /// // * its signal stream will be closed and the default * /// // * implementation will close the WindowActor. * /// // ****************************************************** /// fn finished(&mut self, _ctx: &mut Self::Context) {} /// } /// /// fn create_window_with_rows(factory: &Factories) { /// factory.window.build().actor(|ctx, widgets| { /// for row_number in 0..10 { /// let row_widgets = factory.row.build() /// .connect_tagged_builder_signals(ctx, row_number) /// .widgets().unwrap(); /// row_widgets.label.set_text(&format!("Roe number {}", row_number)); /// widgets.list_box.add(&row_widgets.row); /// } /// WindowActor { widgets } /// }).unwrap(); /// } /// ``` pub struct Factory<A, W, S> { xml: String, _phantom: std::marker::PhantomData<(A, W, S)>, } impl<A, W, S> From<String> for Factory<A, W, S> { fn from(xml: String) -> Self { Self { xml, _phantom: Default::default(), } } } impl<A, W, S> Factory<A, W, S> { /// Create the `gtk::Builder` (inside a [`woab::BuilderUtilizer`](struct.BuilderUtilizer.html)) /// /// Note that this causes the GTK widgets to be created (but not to be shown or be connected to /// anything) pub fn build(&self) -> BuilderUtilizer<A, W, S> { Builder::from_string(&self.xml).into() } } /// Context for utilizing a `gtk::Builder` and connecting it to he Actix world. /// /// See [`woab::Factory`](struct.Factory.html) for usage example. pub struct BuilderUtilizer<A, W, S> { builder: gtk::Builder, _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, _phantom: Default::default(), } } } impl<A, W, S> BuilderUtilizer<A, W, S> where for<'a> &'a gtk::Builder: TryInto<W> { /// Create a widgets struct (as defined by the `W` generic parameter of /// [`woab::Factory`](struct.Factory.html)) and map the builder's widgets to its fields. pub fn widgets(&self) -> Result<W, <>k::Builder as TryInto<W>>::Error> { (&self.builder).try_into() } } impl<A, W, S> BuilderUtilizer<A, W, S> where A: actix::Actor<Context = actix::Context<A>>, for<'a> &'a gtk::Builder: TryInto<W>, S: BuilderSignal, A: actix::StreamHandler<S> { /// Create an Actix actor and connect it to the builder's widgets and signals. /// /// `make_actor` is a function that receives the actor context and the widgets, and is /// responsible for constructing the actor struct with the widgets inside it. It can also be /// used for configuring and or showing the widgets GTK-wise (though this can also be handled /// by the actor afterwards) pub fn actor(&self, make_actor: impl FnOnce(&mut A::Context, W) -> A) -> Result<actix::Addr<A>, <>k::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); make_actor(ctx, widgets) })) } } impl<A, W, S> BuilderUtilizer<A, W, S> where S: BuilderSignal, { /// 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 /// [`woab::Factory`](struct.Factory.html) - if the builder sends signals not covered by /// `S`'s variants they'll be ignored. /// * If the builder defines no signals, or if none of the signals it defines are covered by /// `S`, this method will return `None`. This is important because otherwise it would have /// 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) } /// Stream the signals generated by the builder to an actor represented by `ctx`, together with /// a tag. /// /// When using the same actor to handle multiple copies of the same set of widgets (e.g. /// multiple `GtkListBoxRow`s) the tag can be used to determine which copy generated the /// signal. /// /// **If multiple tagged signals are streamed to the same actor - which is the typical use case /// for tagged signals - `StreamHandler::finished` should be overridden to avoid stopping the /// actor when one instance of the widgets is removed!!!** pub fn connect_tagged_builder_signals<T, C, AA>(&self, ctx: &mut C, tag: T) -> &Self where T: Clone + 'static, AA: actix::Actor<Context = C>, C: actix::AsyncContext<AA>, AA: actix::StreamHandler<(T, S)> { if let Some(rx) = self.stream_builder_signals() { let stream = rx.map(move |s| (tag.clone(), s)); ctx.add_stream(stream); } self } }