1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
15 # Implementation of the app.nit UI module for GNU/Linux
23 # Request width of the GTK window for an app.nit application
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
29 redef fun setup
do gtk_init
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
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
46 bar
.add back_button
.native
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
58 # Button on the header bar to go back
59 var back_button
= new BackButton is lazy
61 # On GNU/Linux, we go through all the callbacks once,
62 # there is no complex life-cycle.
76 # Spacing between GTK controls, default at 2
77 var control_spacing
= 2 is writable
79 redef fun window
=(window
)
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
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
91 native_window
.show_all
95 if window
.enable_back_button
then
96 back_button
.native
.show
97 else back_button
.native
.hide
105 # The GTK element used to implement `self`
106 fun native
: NATIVE is abstract
109 type NATIVE: GtkWidget
114 if not native
.address_is_null
then native
.destroy
118 redef class CompositeControl
121 # On GNU/Linux, a window is implemented by placing the `view` in a `GtkStack` in the single GTK window
124 # Root view of this window
125 var view
: nullable View = null
129 if view
isa View then
131 view
.native
.valign
= new GtkAlign.start
132 view
.native
.set_size_request
(gtk_window_width_request
, 0)
142 redef fun enabled
do return native
.sensitive
143 redef fun enabled
=(enabled
) do native
.sensitive
= enabled
or else true
147 redef type NATIVE: GtkBox
152 if item
isa View then native
.add item
.native
155 redef fun remove
(item
)
158 if item
isa View then native
.remove item
.native
162 redef class HorizontalLayout
163 redef var native
= new GtkBox(new GtkOrientation.horizontal
, app
.control_spacing
)
168 # FIXME abstract the use either homogeneous or weight to balance views size in a layout
169 native
.homogeneous
= true
170 native
.set_child_packing
(item
.native
, true, true, 0, new GtkPackType.start
)
174 redef class VerticalLayout
175 redef var native
= new GtkBox(new GtkOrientation.vertical
, app
.control_spacing
)
181 native
.set_child_packing
(item
.native
, true, true, 0, new GtkPackType.start
)
185 # On GNU/Linux, this is implemented by a `GtkListBox` inside a `GtkScrolledWindow`
186 redef class ListLayout
188 redef type NATIVE: GtkScrolledWindow
190 redef var native
= new GtkScrolledWindow
192 # Container inside `native`
193 var native_list_box
= new GtkListBox
195 # `GtkListBoxRow` used to contains children `View`s
196 var native_rows
= new Map[View, GtkListBoxRow]
199 native_list_box
.selection_mode
= new GtkSelectionMode.none
200 native
.add native_list_box
202 # Set the size of the GtkScrolledWindow:
203 # use content width and set static height
204 native
.set_policy
(new GtkPolicyType.never
, new GtkPolicyType.automatic
)
205 native
.set_size_request
(gtk_window_width_request
, 640)
211 if item
isa View then
212 var native_row
= new GtkListBoxRow
213 #native_row.activable = false # TODO with GTK 3.14
214 #native_row.selectable = false
215 native_row
.add item
.native
217 native_rows
[item
] = native_row
218 native_list_box
.add native_row
223 redef fun remove
(item
)
226 if item
isa View then
227 var native_row
= native_rows
.get_or_null
(item
)
228 if native_row
== null then
229 print_error
"Error: {self} does not contains {item}"
233 native_list_box
.remove native_row
234 native_rows
.keys
.remove item
241 redef type NATIVE: GtkButton
242 redef var native
= new GtkButton
244 redef fun text
do return native
.text
245 redef fun text
=(value
) do native
.text
= (value
or else "").to_s
247 redef fun signal
(sender
, data
) do notify_observers
new ButtonPressEvent(self)
249 init do native
.signal_connect
("clicked", self, null)
252 # Button to go back between windows
257 redef fun text
=(value
) do super(value
or else "Back")
259 redef fun signal
(sender
, data
)
263 app
.window
.on_back_button
268 redef type NATIVE: GtkLabel
269 redef var native
= new GtkLabel("")
271 redef fun text
do return native
.text
273 redef fun text
=(value
)
275 var cfmt
= pango_markup_format
.to_cstring
276 var cvalue
= (value
or else "").to_cstring
277 native
.set_markup
(cfmt
, cvalue
)
280 # Pango format string applied to the `text` attribute
281 var pango_markup_format
= "\%s" is lazy
283 redef fun size
=(size
)
285 if size
== null or size
== 1.0 then
286 pango_markup_format
= "\%s"
287 else if size
< 1.0 then
288 pango_markup_format
= "<span size=\"small\
">\%s</span>"
289 else#if size > 1.0 then
290 pango_markup_format
= "<span size=\"large\
">\%s</span>"
293 # Force reloading `text`
297 redef fun align
=(align
)
299 align
= align
or else 0.0
301 # Set whole label alignement
302 native
.set_alignment
(align
, 0.5)
304 # Set multiline justification
305 native
.justify
= if align
== 0.5 then
306 new GtkJustification.center
307 else if align
< 0.5 then
308 new GtkJustification.left
309 else#if align > 0.5 then
310 new GtkJustification.right
315 redef type NATIVE: GtkCheckButton
316 redef var native
= new GtkCheckButton
318 redef fun signal
(sender
, data
) do notify_observers
new ToggleEvent(self)
319 init do native
.signal_connect
("toggled", self, null)
321 redef fun text
do return native
.text
322 redef fun text
=(value
) do native
.text
= (value
or else "").to_s
324 redef fun is_checked
do return native
.active
325 redef fun is_checked
=(value
) do native
.active
= value
328 redef class TextInput
329 redef type NATIVE: GtkEntry
330 redef var native
= new GtkEntry
332 redef fun text
do return native
.text
333 redef fun text
=(value
) do
334 if value
== null then value
= ""
335 native
.text
= value
.to_s
338 redef fun is_password
=(value
)
340 native
.visibility
= value
!= true
346 redef fun open_in_browser
do system
("xdg-open '{self.escape_to_sh}' &")