Implementation of the app.nit UI module for GNU/Linux

Introduced classes

class BackButton

linux :: BackButton

Button to go back between windows

Redefined classes

redef class App

linux :: ui $ App

App subclasses are cross-platform applications
redef class Button

linux :: ui $ Button

A pressable button, raises ButtonPressEvent
redef class CheckBox

linux :: ui $ CheckBox

Toggle control between two states, also displays a label
redef class CompositeControl

linux :: ui $ CompositeControl

A Control grouping other controls
redef class Control

linux :: ui $ Control

A control implementing the UI
redef class HorizontalLayout

linux :: ui $ HorizontalLayout

An horizontal linear organization
redef class Label

linux :: ui $ Label

A simple text label
redef abstract class Layout

linux :: ui $ Layout

A layout to visually organize Controls
redef class ListLayout

linux :: ui $ ListLayout

On GNU/Linux, this is implemented by a GtkListBox inside a GtkScrolledWindow
redef class Sys

linux :: ui $ Sys

The main class of the program.
redef abstract class Text

linux :: ui $ Text

High-level abstraction for all text representations
redef class TextInput

linux :: ui $ TextInput

A control for the user to enter custom text
redef class VerticalLayout

linux :: ui $ VerticalLayout

A vertical linear organization
redef abstract class View

linux :: ui $ View

A visible Control
redef class Window

linux :: ui $ Window

On GNU/Linux, a window is implemented by placing the view in a GtkStack in the single GTK window

All class definitions

redef class App

linux :: ui $ App

App subclasses are cross-platform applications
class BackButton

linux $ BackButton

Button to go back between windows
redef class Button

linux :: ui $ Button

A pressable button, raises ButtonPressEvent
redef class CheckBox

linux :: ui $ CheckBox

Toggle control between two states, also displays a label
redef class CompositeControl

linux :: ui $ CompositeControl

A Control grouping other controls
redef class Control

linux :: ui $ Control

A control implementing the UI
redef class HorizontalLayout

linux :: ui $ HorizontalLayout

An horizontal linear organization
redef class Label

linux :: ui $ Label

A simple text label
redef abstract class Layout

linux :: ui $ Layout

A layout to visually organize Controls
redef class ListLayout

linux :: ui $ ListLayout

On GNU/Linux, this is implemented by a GtkListBox inside a GtkScrolledWindow
redef class Sys

linux :: ui $ Sys

The main class of the program.
redef abstract class Text

linux :: ui $ Text

High-level abstraction for all text representations
redef class TextInput

linux :: ui $ TextInput

A control for the user to enter custom text
redef class VerticalLayout

linux :: ui $ VerticalLayout

A vertical linear organization
redef abstract class View

linux :: ui $ View

A visible Control
redef class Window

linux :: ui $ Window

On GNU/Linux, a window is implemented by placing the view in a GtkStack in the single GTK window
package_diagram linux::ui ui app::ui ui linux::ui->app::ui gtk::v3_10 v3_10 linux::ui->gtk::v3_10 linux::data_store data_store linux::ui->linux::data_store app::app_base app_base app::ui->app::app_base gtk::v3_8 v3_8 gtk::v3_10->gtk::v3_8 app::data_store data_store linux::data_store->app::data_store xdg_basedir xdg_basedir linux::data_store->xdg_basedir sqlite3 sqlite3 linux::data_store->sqlite3 json json linux::data_store->json ...app::app_base ... ...app::app_base->app::app_base ...gtk::v3_8 ... ...gtk::v3_8->gtk::v3_8 ...app::data_store ... ...app::data_store->app::data_store ...xdg_basedir ... ...xdg_basedir->xdg_basedir ...sqlite3 ... ...sqlite3->sqlite3 ...json ... ...json->json a_star-m a_star-m a_star-m->linux::ui

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module app_base

app :: app_base

Base of the app.nit framework, defines App
module array

core :: array

This module introduces the standard array structure.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module caching

serialization :: caching

Services for caching serialization engines
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module data_store

app :: data_store

Key/value storage services
module engine_tools

serialization :: engine_tools

Advanced services for serialization engines
module environ

core :: environ

Access to the environment variables of the process
module error

json :: error

Intro JsonParseError which is exposed by all JSON reading APIs
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module gdk

gtk :: gdk

Services from GDK
module gdk_enums

gtk :: gdk_enums

module gtk_core

gtk :: gtk_core

Classes and services to use libGTK widgets
module gtk_enums

gtk :: gtk_enums

module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module inspect

serialization :: inspect

Refine Serializable::inspect to show more useful information
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module json

json :: json

Read and write JSON formatted text using the standard serialization services
module kernel

core :: kernel

Most basic classes and methods.
module list

core :: list

This module handle double linked lists
module math

core :: math

Mathematical operations
module meta

meta :: meta

Simple user-defined meta-level to manipulate types of instances as object.
module native

core :: native

Native structures for text and bytes
module native_sqlite3

sqlite3 :: native_sqlite3

Low-level Sqlite3 features
module numeric

core :: numeric

Advanced services for Numeric types
module parser_base

parser_base :: parser_base

Simple base for hand-made parsers of all kinds
module poset

poset :: poset

Pre order sets and partial order set (ie hierarchies)
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module safe

serialization :: safe

Services for safer deserialization engines
module serialization

serialization :: serialization

General serialization services
module serialization_core

serialization :: serialization_core

Abstract services to serialize Nit objects to different formats
module serialization_read

json :: serialization_read

Services to read JSON: deserialize_json and JsonDeserializer
module serialization_write

json :: serialization_write

Services to write Nit objects to JSON strings: serialize_to_json and JsonSerializer
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module sqlite3

sqlite3 :: sqlite3

Services to manipulate a Sqlite3 database
module static

json :: static

Static interface to read Nit objects from JSON strings
module stream

core :: stream

Input and output streams of characters
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O
module v3_4

gtk :: v3_4

module v3_6

gtk :: v3_6

GTK+ services added at version 3.6
module v3_8

gtk :: v3_8

GTK+ services added at version 3.8
module xdg_basedir

xdg_basedir :: xdg_basedir

Services for using the XDG Base Directory specification

Parents

module data_store

linux :: data_store

app::data_store implementation on GNU/Linux
module ui

app :: ui

Portable UI controls for mobiles apps
module v3_10

gtk :: v3_10

GTK+ services added at version 3.10

Children

module a_star-m

a_star-m

# Implementation of the app.nit UI module for GNU/Linux
module ui

import app::ui
import gtk::v3_10

import data_store

# Request width of the GTK window for an app.nit application
#
# This is the minimum width of the window, it may grow bigger to fit content.
fun gtk_window_width_request: Int do return 480

redef class App
	redef fun setup do gtk_init

	# Single GTK window of this application
	var native_window: GtkWindow is lazy do
		var win = new GtkWindow(new GtkWindowType.toplevel)
		win.connect_destroy_signal_to_quit
		win.titlebar = native_header_bar
		win.add native_stack
		return win
	end

	# GTK 3 header bar
	var native_header_bar: GtkHeaderBar is lazy do
		var bar = new GtkHeaderBar
		bar.title = "app.nit" # TODO offer a portable API to name windows
		bar.show_close_button = true

		bar.add back_button.native

		return bar
	end

	# Root `GtkStack` used to simulate the many app.nit windows
	var native_stack: GtkStack is lazy do
		var stack = new GtkStack
		stack.homogeneous = false
		return stack
	end

	# Button on the header bar to go back
	var back_button = new BackButton is lazy

	# On GNU/Linux, we go through all the callbacks once,
	# there is no complex life-cycle.
	redef fun run
	do
		app.on_create
		app.on_restore_state
		app.on_resume

		gtk_main

		app.on_pause
		app.on_stop
		app.on_save_state
	end

	# Spacing between GTK controls, default at 2
	var control_spacing = 2 is writable

	redef fun window=(window)
	do
		var root_view = window.view
		assert root_view != null
		native_stack.add root_view.native
		native_stack.visible_child = root_view.native

		# FIXME These settings forces the GTK window to resize to its minimum
		# size when changing app.nit windows. It is not pretty, but it could be
		# improved with GTK 3.18 and interpolate_size.
		native_window.resizable = false

		native_window.show_all

		super

		if window.enable_back_button then
			back_button.native.show
		else back_button.native.hide
	end
end

redef class Control
	super GtkCallable
	super Finalizable

	# The GTK element used to implement `self`
	fun native: NATIVE is abstract

	# Type of `native`
	type NATIVE: GtkWidget

	redef fun finalize
	do
		var native = native
		if not native.address_is_null then native.destroy
	end
end

redef class CompositeControl
end

# On GNU/Linux, a window is implemented by placing the `view` in a `GtkStack` in the single GTK window
redef class Window

	# Root view of this window
	var view: nullable View = null

	redef fun add(view)
	do
		if view isa View then
			self.view = view
			view.native.valign = new GtkAlign.start
			view.native.set_size_request(gtk_window_width_request, 0)
		end

		super
	end
end

redef class View
	init do native.show

	redef fun enabled do return native.sensitive
	redef fun enabled=(enabled) do native.sensitive = enabled or else true
end

redef class Layout
	redef type NATIVE: GtkBox

	redef fun add(item)
	do
		super
		if item isa View then native.add item.native
	end

	redef fun remove(item)
	do
		super
		if item isa View then native.remove item.native
	end
end

redef class HorizontalLayout
	redef var native = new GtkBox(new GtkOrientation.horizontal, app.control_spacing)

	redef fun add(item)
	do
		super
		# FIXME abstract the use either homogeneous or weight to balance views size in a layout
		native.homogeneous = true
		native.set_child_packing(item.native, true, true, 0, new GtkPackType.start)
	end
end

redef class VerticalLayout
	redef var native = new GtkBox(new GtkOrientation.vertical, app.control_spacing)

	redef fun add(item)
	do
		super

		native.set_child_packing(item.native, true, true, 0, new GtkPackType.start)
	end
end

# On GNU/Linux, this is implemented by a `GtkListBox` inside a `GtkScrolledWindow`
redef class ListLayout

	redef type NATIVE: GtkScrolledWindow

	redef var native = new GtkScrolledWindow

	# Container inside `native`
	var native_list_box = new GtkListBox

	# `GtkListBoxRow` used to contains children `View`s
	var native_rows = new Map[View, GtkListBoxRow]

	init do
		native_list_box.selection_mode = new GtkSelectionMode.none
		native.add native_list_box

		# Set the size of the GtkScrolledWindow:
		# use content width and set static height
		native.set_policy(new GtkPolicyType.never, new GtkPolicyType.automatic)
		native.set_size_request(gtk_window_width_request, 640)
	end

	redef fun add(item)
	do
		super
		if item isa View then
			var native_row = new GtkListBoxRow
			#native_row.activable = false # TODO with GTK 3.14
			#native_row.selectable = false
			native_row.add item.native

			native_rows[item] = native_row
			native_list_box.add native_row
			native_row.show
		end
	end

	redef fun remove(item)
	do
		super
		if item isa View then
			var native_row = native_rows.get_or_null(item)
			if native_row == null then
				print_error "Error: {self} does not contains {item}"
				return
			end

			native_list_box.remove native_row
			native_rows.keys.remove item
			native_row.destroy
		end
	end
end

redef class Button
	redef type NATIVE: GtkButton
	redef var native = new GtkButton

	redef fun text do return native.text
	redef fun text=(value) do native.text = (value or else "").to_s

	redef fun signal(sender, data) do notify_observers new ButtonPressEvent(self)

	init do native.signal_connect("clicked", self, null)
end

# Button to go back between windows
class BackButton
	super Button

	# TODO i18n
	redef fun text=(value) do super(value or else "Back")

	redef fun signal(sender, data)
	do
		super

		app.window.on_back_button
	end
end

redef class Label
	redef type NATIVE: GtkLabel
	redef var native = new GtkLabel("")

	redef fun text do return native.text

	redef fun text=(value)
	do
		var cfmt = pango_markup_format.to_cstring
		var cvalue = (value or else "").to_cstring
		native.set_markup(cfmt, cvalue)
	end

	# Pango format string applied to the `text` attribute
	var pango_markup_format = "\%s" is lazy

	redef fun size=(size)
	do
		if size == null or size == 1.0 then
			pango_markup_format = "\%s"
		else if size < 1.0 then
			pango_markup_format = "<span size=\"small\">\%s</span>"
		else#if size > 1.0 then
			pango_markup_format = "<span size=\"large\">\%s</span>"
		end

		# Force reloading `text`
		text = text
	end

	redef fun align=(align)
	do
		align = align or else 0.0

		# Set whole label alignement
		native.set_alignment(align, 0.5)

		# Set multiline justification
		native.justify = if align == 0.5 then
			new GtkJustification.center
		else if align < 0.5 then
			new GtkJustification.left
		else#if align > 0.5 then
			new GtkJustification.right
	end
end

redef class CheckBox
	redef type NATIVE: GtkCheckButton
	redef var native = new GtkCheckButton

	redef fun signal(sender, data) do notify_observers new ToggleEvent(self)
	init do native.signal_connect("toggled", self, null)

	redef fun text do return native.text
	redef fun text=(value) do native.text = (value or else "").to_s

	redef fun is_checked do return native.active
	redef fun is_checked=(value) do native.active = value
end

redef class TextInput
	redef type NATIVE: GtkEntry
	redef var native = new GtkEntry

	redef fun text do return native.text
	redef fun text=(value) do
		if value == null then value = ""
		native.text = value.to_s
	end

	redef fun is_password=(value)
	do
		native.visibility = value != true
		super
	end
end

redef class Text
	redef fun open_in_browser do system("xdg-open '{self.escape_to_sh}' &")
end
lib/linux/ui.nit:15,1--347,3