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
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316

pub mod macros {

	// A helper macro to find Builders for Widgets etc.
	#[doc(hidden)]
	#[macro_export]
	macro_rules! __grade_find_builder_internal {
	    (AboutDialog) => {gtk::AboutDialogBuilder};
	    (AccelLabel) => {gtk::AccelLabelBuilder};
	    (ActionBar) => {gtk::ActionBarBuilder};
	    (AppChooserButton) => {gtk::AppChooserButtonBuilder};
	    (AppChooserDialog) => {gtk::AppChooserDialogBuilder};
	    (AppChooserWidget) => {gtk::AppChooserWidgetBuilder};
	    (Application) => {gtk::ApplicationBuilder};
	    (ApplicationWindow) => {gtk::ApplicationWindowBuilder};
	    (AspectFrame) => {gtk::AspectFrameBuilder};
	    (Assistant) => {gtk::AssistantBuilder};
	    (Box) => {gtk::BoxBuilder};
	    (Button) => {gtk::ButtonBuilder};
	    (ButtonBox) => {gtk::ButtonBoxBuilder};
	    (Calendear) => {gtk::CalendearBuilder};
	    (CellAreaBox) => {gtk::CellAreaBoxBuilder};
	    (CellRendererAccel) => {gtk::CellRendererAccelBuilder};
	    (CellRendererCombo) => {gtk::CellRendererComboBuilder};
	    (CellRendererPixbuf) => {gtk::CellRendererPixbufBuilder};
	    (CellRendererProgress) => {gtk::CellRendererProgressBuilder};
	    (CellRendererSpin) => {gtk::CellRendererSpinBuilder};
	    (CellRendererSpinner) => {gtk::CellRendererSpinnerBuilder};
	    (CellRendererText) => {gtk::CellRendererTextBuilder};
	    (CellRendererToggle) => {gtk::CellRendererToggleBuilder};
	    (CellView) => {gtk::CellViewBuilder};
	    (CheckButton) => {gtk::CheckButtonBuilder};
	    (CheckMenuItem) => {gtk::CheckMenuItemBuilder};
	    (ColorButton) => {gtk::ColorButtonBuilder};
	    (ColorChooserDialog) => {gtk::ColorChooserDialogBuilder};
	    (ColorChooserWidget) => {gtk::ColorChooserWidgetBuilder};
	    (ComboBox) => {gtk::ComboBoxBuilder};
	    (ComboBoxText) => {gtk::ComboBoxTextBuilder};
	    (Dialog) => {gtk::DialogBuilder};
	    (DrawingArea) => {gtk::DrawingAreaBuilder};
	    (Entry) => {gtk::EntryBuilder};
	    (EntryCompletion) => {gtk::EntryCompletionBuilder};
	    (EventBox) => {gtk::EventBoxBuilder};
	    (Expander) => {gtk::ExpanderBuilder};
	    (FileChooserButton) => {gtk::FileChooserButtonBuilder};
	    (FileChooserDialog) => {gtk::FileChooserDialogBuilder};
	    (FileChooserNative) => {gtk::FileChooserNativeBuilder};
	    (FileChooserWidget) => {gtk::FileChooserWidgetBuilder};
	    (FlowBox) => {gtk::FlowBoxBuilder};
	    (FlowBoxChild) => {gtk::FlowBoxChildBuilder};
	    (FontButtonBuilder) => {gtk::FontButtonBuilderBuilder};
	    (FontChooserDialog) => {gtk::FontChooserDialogBuilder};
	    (FontChooserWidget) => {gtk::FontChooserWidgetBuilder};
	    (Frame) => {gtk::FrameBuilder};
	    (GLArea) => {gtk::GLAreaBuilder};
	    (GestureDrag) => {gtk::GestureDragBuilder};
	    (GestureLongPress) => {gtk::GestureLongPressBuilder};
	    (GestureMultiPress) => {gtk::GestureMultiPressBuilder};
	    (GesturePan) => {gtk::GesturePanBuilder};
	    (GestureRotate) => {gtk::GestureRotateBuilder};
	    (GestureSwipe) => {gtk::GestureSwipeBuilder};
	    (GestureZoom) => {gtk::GestureZoomBuilder};
	    (Grid) => {gtk::GridBuilder};
	    (HeaderBar) => {gtk::HeaderBarBuilder};
	    (IMContextSimple) => {gtk::IMContextSimpleBuilder};
	    (IMMulticontext) => {gtk::IMMulticontextBuilder};
	    (Image) => {gtk::ImageBuilder};
	    (InfoBar) => {gtk::InfoBarBuilder};
	    (Invisible) => {gtk::InvisibleBuilder};
	    (Label) => {gtk::LabelBuilder};
	    (Layout) => {gtk::LayoutBuilder};
	    (LevelBar) => {gtk::LevelBarBuilder};
	    (LinkButton) => {gtk::LinkButtonBuilder};
	    (ListBox) => {gtk::ListBoxBuilder};
	    (ListBoxRow) => {gtk::ListBoxRowBuilder};
	    (LockButton) => {gtk::LockButtonBuilder};
	    (MenuBar) => {gtk::MenuBarBuilder};
	    (MenuButton) => {gtk::MenuButtonBuilder};
	    (MenuItem) => {gtk::MenuItemBuilder};
	    (MenuToolButton) => {gtk::MenuToolButtonBuilder};
	    (MessageDialog) => {gtk::MessageDialogBuilder};
	    (ModelButton) => {gtk::ModelButtonBuilder};
	    (MountOperation) => {gtk::MountOperationBuilder};
	    (Notenook) => {gtk::NotenookBuilder};
	    (OffscreenWindow) => {gtk::OffscreenWindowBuilder};
	    (Overlay) => {gtk::OverlayBuilder};
	    (PadController) => {gtk::PadControllerBuilder};
	    (Paned) => {gtk::PanedBuilder};
	    (PlacesSidebar) => {gtk::PlacesSidebarBuilder};
	    (Plug) => {gtk::PlugBuilder};
	    (Popover) => {gtk::PopoverBuilder};
	    (PopoverMenu) => {gtk::PopoverMenuBuilder};
	    (PrintOperation) => {gtk::PrintOperationBuilder};
	    (ProgressBar) => {gtk::ProgressBarBuilder};
	    (RadioButton) => {gtk::RadioButtonBuilder};
	    (RadioMenuItem) => {gtk::RadioMenuItemBuilder};
	    (RadioToolButton) => {gtk::RadioToolButtonBuilder};
	    (RecentChooserDialog) => {gtk::RecentChooserDialogBuilder};
	    (RecentChooserMenu) => {gtk::RecentChooserMenuBuilder};
	    (RecentChooserWidget) => {gtk::RecentChooserWidgetBuilder};
	    (RecentManager) => {gtk::RecentManagerBuilder};
	    (Revealer) => {gtk::RevealerBuilder};
	    (Scale) => {gtk::ScaleBuilder};
	    (ScaleButton) => {gtk::ScaleButtonBuilder};
	    (Scrollbar) => {gtk::ScrollbarBuilder};
	    (ScrolledWindow) => {gtk::ScrolledWindowBuilder};
	    (SearchBar) => {gtk::SearchBarBuilder};
	    (SearchEntry) => {gtk::SearchEntryBuilder};
	    (Separator) => {gtk::SeparatorBuilder};
	    (SeparatorMenuItem) => {gtk::SeparatorMenuItemBuilder};
	    (SeparatorToolItem) => {gtk::SeparatorToolItemBuilder};
	    (SizeGroup) => {gtk::SizeGroupBuilder};
	    (Socket) => {gtk::SocketBuilder};
	    (SpinButton) => {gtk::SpinButtonBuilder};
	    (Spinner) => {gtk::SpinnerBuilder};
	    (Stack) => {gtk::StackBuilder};
	    (StackSidebar) => {gtk::BuilderStackSidebar};
	    (StackSwitcher) => {gtk::StackSwitcherBuilder};
	    (Statusbar) => {gtk::StatusbarBuilder};
	    (StyleContext) => {gtk::StyleContextBuilder};
	    (Switch) => {gtk::SwitchBuilder};
	    (TextBuffer) => {gtk::TextBufferBuilder};
	    (TextTag) => {gtk::TextTagBuilder};
	    (TextView) => {gtk::TextViewBuilder};
	    (ToggleButton) => {gtk::ToggleButtonBuilder};
	    (ToggleToolButton) => {Tgtk::oggleToolButtonBuilder};
	    (ToolButton) => {gtk::ToolButtonBuilder};
	    (ToolItem) => {gtk::ToolItemBuilder};
	    (ToolItemGroup) => {gtk::ToolItemGroupBuilder};
	    (ToolPalette) => {gtk::ToolPaletteBuilder};
	    (Toolbar) => {gtk::ToolbarBuilder};
	    (TreeView) => {gtk::TreeViewBuilder};
	    (TreeViewColumn) => {gtk::TreeViewColumnBuilder};
	    (Viewport) => {gtk::ViewportBuilder};
	    (VolumeButton) => {gtk::VolumeButtonBuilder};
	    (Window) => {gtk::WindowBuilder};
	}

	// A helper macro to find Builders for Widgets etc.
	#[doc(hidden)]
	#[macro_export]
	macro_rules! __grade_find_builder {
	    ($n:ident:$p:ident) => {$crate::__grade_find_builder_internal!($p)};
	    ($p:ident) => {$crate::__grade_find_builder_internal!($p)};
	}

	// A helper macro to make parent widgets identifiable
	#[doc(hidden)]
	#[macro_export]
	macro_rules! __grade_link_to_name {
	   ($n:ident:$p:ident, $parent:ident) => {let $n = & $parent;};
	   ($p:ident, $parent:ident) => {};
	}

	#[macro_export]
	macro_rules! __build_func {
	    ($parent:ident, [$pack_func:ident $(,$pack_arg:expr)*], $child:ident$(:$child_alias:ident)? {$($child_body:tt)*}) => {
	        $parent.$pack_func(
	            & $crate::build! {$child$(:$child_alias)? {$($child_body)*}}.upcast::<gtk::Widget>(),
	                $($pack_arg),*
	        );
	    };
	    ($parent:ident, $child:ident$(:$child_alias:ident)? {$($child_body:tt)*}) => {
	        $parent.add(
	            & build! { $child$(:$child_alias)? { $($child_body)* } }.upcast::<gtk::Widget>()
	        );
	    };
	}

	// A helper macro to connect signals in widgets
	#[doc(hidden)]
	#[macro_export]
	macro_rules! __grade_connect_signal {
	    ($grade_widget:ident, notify($field:ident), $callback:expr) => { let _ = $grade_widget.connect_notify(Some("$field"), $callback); }; 
	    ($grade_widget:ident, $signal:path, $callback:expr) => { let _ =$grade_widget.connect("$signal", true, $callback);};
	}

	/// Instantiate a buildable Object from an inline macro
	#[macro_export]
	macro_rules! build {
	    { 
	        $parent:ident$(:$parent_alias:ident)? {
	            $( $field:ident:$val:expr, )*
	            $( => $signal:path: $callback:expr, )*
	            $( -- $([$pack_func:ident $(,$pack_arg:expr)*])? $child:ident$(:$child_alias:ident)? {$($child_body:tt)*})*
	            $( .. $meth:ident($($meth_body:expr),*) )*
	        } 
	    } => {
	        {

	           #[allow(unused_mut)] let mut builder = <$crate::__grade_find_builder!($parent$(:$parent_alias)?)>::new();
	           $(builder = builder.$field($val); )*
	           let grade_widget = builder.build();
	           $crate::__grade_link_to_name!($parent$(:$parent_alias)?, grade_widget);
	           $( $crate::__build_func!(grade_widget, $([$pack_func $(,$pack_arg)*],)? $child$(:$child_alias)? {$($child_body)*}); )*
	           $( grade_widget.$meth($($meth_body),*); )*
	           $( $crate::__grade_connect_signal!(grade_widget, $signal, $callback);)*
	           grade_widget
	        }
	    };
	}
}


#[cfg(test)]
mod tests {
	use gtk::*;
	use pango::EllipsizeMode;
	use crate::build;

	macro_rules! assert_gtype {
		($x:expr, $typ:ty) => (assert!($x.clone().dynamic_cast::<$typ>().is_ok()););
	}


    #[test]
    fn inline_application() {
    	let app = Application::new(Some("com.github.grade.test.inline_application"), Default::default()).unwrap();

        let grade_window = build! {
            ApplicationWindow {
                application: &app,
                show_menubar: false,
                default_height: 300,
                default_width: 400,

                 => show_menubar_notify: |_win| {None},


                -- Viewport {
                	-- Grid {
                		hexpand: true,
                		vexpand: true,

                		// grid children
                		-- [attach, 1, 3, 1, 1 ] _left_button:Button {
                			vexpand: true,
                			label: "Left",
                			name: "label_left",
                		}

                		-- [attach, 3, 3, 1, 1 ] _right_button:Button {
                			label: "Right",
                			name: "label_right",
                		}

                		-- [attach, 2, 3, 1, 1 ] Label {
                			label: "This is a Label, amazing!",
                			ellipsize: EllipsizeMode::End,

                		}
                		/*some comment */
                		..attach(
                			& build! {
                				TextView {
                					buffer: & build! {
                						TextBuffer {
                							text: "This is a textview, look at it and despair!",
                						}
                					}, 
                				}
                			}.upcast::<Widget>(), 1+0, 1, 2+1, 1+1
                		)
                	}
                }

                ..set_titlebar( Some( &build! {
                	HeaderBar {
                		title: "A grade test app",

                		-- [pack_start] Button {
                			label: "Start",
                		}

                		-- [pack_end] _end_button:Button {
                			label: "End",
                		}
                	}
                }.upcast::<Widget>()))
            }
        };

        assert_gtype!(&grade_window, ApplicationWindow);
        
        let hbar = (&grade_window.get_titlebar().unwrap()).clone();
        let grid = &grade_window.get_child().unwrap().downcast::<Viewport>().unwrap().get_child().unwrap().downcast::<Grid>().unwrap().clone();
        let left_button = &grid.get_child_at(1,3).unwrap().downcast::<Button>().unwrap().clone();
        let mid_label = &grid.get_child_at(2,3).unwrap().downcast::<Label>().unwrap().clone();
        let right_button = &grid.get_child_at(3,3).unwrap().downcast::<Button>().unwrap().clone();
        let tv = &grid.get_child_at(1,1).unwrap().downcast::<TextView>().unwrap().clone();
        let (start_iter, end_iter) = &tv.get_buffer().unwrap().get_bounds();

        assert_eq!(hbar.clone().downcast::<HeaderBar>().unwrap().get_children().len(), 2);
        assert_eq!(hbar.clone().downcast::<HeaderBar>().unwrap().get_children()[0].clone().downcast::<Button>().unwrap().get_label().unwrap(), "Start");
        assert_eq!(hbar.clone().downcast::<HeaderBar>().unwrap().get_children()[1].clone().downcast::<Button>().unwrap().get_label().unwrap(), "End");
        assert_eq!(grid.get_children().len(), 4);
        println!("{:?}", &grid);
        assert_eq!(left_button.get_label().unwrap(),"Left");
        assert_eq!(WidgetExt::get_name(left_button).unwrap(),"label_left");
        assert_eq!(right_button.get_label().unwrap(),"Right");
        assert_eq!(WidgetExt::get_name(right_button).unwrap(),"label_right");
        assert_eq!(mid_label.get_label().unwrap(), "This is a Label, amazing!");
        assert_eq!(mid_label.get_ellipsize(), EllipsizeMode::End);
        assert_eq!(grid.get_cell_height(&left_button.clone()), 1);
        assert_eq!(grid.get_cell_height(&right_button.clone()), 1);
        assert_eq!(grid.get_cell_height(&mid_label.clone()), 1);
        assert_eq!(grid.get_cell_height(&tv.clone()), 2);
        assert_eq!(grid.get_cell_width(&left_button.clone()), 1);
        assert_eq!(grid.get_cell_width(&right_button.clone()), 1);
        assert_eq!(grid.get_cell_width(&mid_label.clone()), 1);
        assert_eq!(grid.get_cell_width(&tv.clone()), 3);
        assert_eq!(tv.get_buffer().unwrap().get_text(start_iter, end_iter, true).unwrap(), "This is a textview, look at it and despair!");

    }
}