Merge: app.nit on GNU/Linux: implement multiple windows support using a GtkStack
[nit.git] / lib / linux / ui.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Implementation of the app.nit UI module for GNU/Linux
16 module ui
17
18 import app::ui
19 import gtk::v3_10
20
21 import data_store
22
23 # Request width of the GTK window for an app.nit application
24 #
25 # This is the minimum width of the window, it may grow bigger to fit content.
26 fun gtk_window_width_request: Int do return 480
27
28 redef class App
29 redef fun setup do gtk_init
30
31 # Single GTK window of this application
32 var native_window: GtkWindow is lazy do
33 var win = new GtkWindow(new GtkWindowType.toplevel)
34 win.connect_destroy_signal_to_quit
35 win.titlebar = native_header_bar
36 win.add native_stack
37 return win
38 end
39
40 # GTK 3 header bar
41 var native_header_bar: GtkHeaderBar is lazy do
42 var bar = new GtkHeaderBar
43 bar.title = "app.nit" # TODO offer a portable API to name windows
44 bar.show_close_button = true
45
46 # TODO add back button
47
48 return bar
49 end
50
51 # Root `GtkStack` used to simulate the many app.nit windows
52 var native_stack: GtkStack is lazy do
53 var stack = new GtkStack
54 stack.homogeneous = false
55 return stack
56 end
57
58 # On GNU/Linux, we go through all the callbacks once,
59 # there is no complex life-cycle.
60 redef fun run
61 do
62 app.on_create
63 app.on_restore_state
64 app.on_start
65 app.on_resume
66
67 native_window.show_all
68 gtk_main
69
70 app.on_pause
71 app.on_stop
72 app.on_save_state
73 app.on_destroy
74 end
75
76 # Spacing between GTK controls, default at 2
77 var control_spacing = 2 is writable
78
79 redef fun window=(window)
80 do
81 var root_view = window.view
82 assert root_view != null
83 native_stack.add root_view.native
84 native_stack.visible_child = root_view.native
85
86 # FIXME These settings forces the GTK window to resize to its minimum
87 # size when changing app.nit windows. It is not pretty, but it could be
88 # improved with GTK 3.18 and interpolate_size.
89 native_window.resizable = false
90
91 super
92 end
93 end
94
95 redef class Control
96 super GtkCallable
97 super Finalizable
98
99 # The GTK element used to implement `self`
100 fun native: NATIVE is abstract
101
102 # Type of `native`
103 type NATIVE: GtkWidget
104
105 redef fun finalize
106 do
107 var native = native
108 if not native.address_is_null then native.destroy
109 end
110 end
111
112 redef class CompositeControl
113 end
114
115 # On GNU/Linux, a window is implemented by placing the `view` in a `GtkStack` in the single GTK window
116 redef class Window
117
118 # Root view of this window
119 var view: nullable View = null
120
121 redef fun add(view)
122 do
123 if view isa View then
124 self.view = view
125 view.native.valign = new GtkAlign.start
126 view.native.set_size_request(gtk_window_width_request, 0)
127 end
128
129 super
130 end
131 end
132
133 redef class View
134 init do native.show
135
136 redef fun enabled do return native.sensitive
137 redef fun enabled=(enabled) do native.sensitive = enabled or else true
138 end
139
140 redef class Layout
141 redef type NATIVE: GtkBox
142
143 redef fun add(item)
144 do
145 super
146 if item isa View then native.add item.native
147 end
148
149 redef fun remove(item)
150 do
151 super
152 if item isa View then native.remove item.native
153 end
154 end
155
156 redef class HorizontalLayout
157 redef var native = new GtkBox(new GtkOrientation.horizontal, app.control_spacing)
158
159 redef fun add(item)
160 do
161 super
162 # FIXME abstract the use either homogeneous or weight to balance views size in a layout
163 native.homogeneous = true
164 native.set_child_packing(item.native, true, true, 0, new GtkPackType.start)
165 end
166 end
167
168 redef class VerticalLayout
169 redef var native = new GtkBox(new GtkOrientation.vertical, app.control_spacing)
170
171 redef fun add(item)
172 do
173 super
174
175 native.set_child_packing(item.native, true, true, 0, new GtkPackType.start)
176 end
177 end
178
179 # On GNU/Linux, this is implemented by a `GtkListBox` inside a `GtkScrolledWindow`
180 redef class ListLayout
181
182 redef type NATIVE: GtkScrolledWindow
183
184 redef var native = new GtkScrolledWindow
185
186 # Container inside `native`
187 var native_list_box = new GtkListBox
188
189 init do
190 native_list_box.selection_mode = new GtkSelectionMode.none
191 native.add native_list_box
192
193 # Set the size of the GtkScrolledWindow:
194 # use content width and set static height
195 native.set_policy(new GtkPolicyType.never, new GtkPolicyType.automatic)
196 native.set_size_request(gtk_window_width_request, 640)
197 end
198
199 redef fun add(item)
200 do
201 super
202 if item isa View then native_list_box.add item.native
203 end
204
205 redef fun remove(item)
206 do
207 super
208 if item isa View then native_list_box.remove item.native
209 end
210 end
211
212 redef class Button
213 redef type NATIVE: GtkButton
214 redef var native = new GtkButton
215
216 redef fun text do return native.text
217 redef fun text=(value) do native.text = (value or else "").to_s
218
219 redef fun signal(sender, data) do notify_observers new ButtonPressEvent(self)
220
221 init do native.signal_connect("clicked", self, null)
222 end
223
224 redef class Label
225 redef type NATIVE: GtkLabel
226 redef var native = new GtkLabel("")
227
228 redef fun text do return native.text
229 redef fun text=(value) do native.text = (value or else "").to_s
230 end
231
232 redef class CheckBox
233 redef type NATIVE: GtkCheckButton
234 redef var native = new GtkCheckButton
235
236 redef fun text do return native.text
237 redef fun text=(value) do native.text = (value or else "").to_s
238
239 redef fun is_checked do return native.active
240 redef fun is_checked=(value) do native.active = value
241 end
242
243 redef class TextInput
244 redef type NATIVE: GtkEntry
245 redef var native = new GtkEntry
246
247 redef fun text do return native.text
248 redef fun text=(value) do
249 if value == null then value = ""
250 native.text = value.to_s
251 end
252
253 redef fun is_password=(value)
254 do
255 native.visibility = value != true
256 super
257 end
258 end