Merge: Intro `Task` and use in for Android's `run_on_ui_thread`
authorJean Privat <jean@pryen.org>
Thu, 5 Nov 2015 15:13:53 +0000 (10:13 -0500)
committerJean Privat <jean@pryen.org>
Thu, 5 Nov 2015 15:13:53 +0000 (10:13 -0500)
The `Task` interface defines a fragment of Nit code to execute in its `main` method. This interface should be compatible between different platforms and parallelization engines.

`NativeActivity::run_on_ui_thread` is widely used on Android as most logic is executed on background threads but only the UI thread can modify the UI. It is a perfect example of how the `Task` interface can be used.

Future work include using `Task` in `pthreads` and `gtk::gdk`. But there a a few differences in the logic that makes it not as easy: `Thread::main` returns a `nullable object`, but is it worth keeping it? `GdkRunnable::run` returns a boolean to tell GDK if it should be called again.

Pull-Request: #1812
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Romain Chanoir <romain.chanoir@viacesi.fr>

VERSION
lib/gtk/v3_10.nit [new file with mode: 0644]
lib/gtk/v3_4/gdk.nit [new file with mode: 0644]
lib/gtk/v3_4/gtk_assistant.nit
lib/gtk/v3_4/gtk_core.nit
lib/gtk/v3_4/v3_4.nit
lib/linux/ui.nit
tests/test_gtk.nit

diff --git a/VERSION b/VERSION
index 151a686..a502cc6 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-v0.7.8
+v0.7.9
diff --git a/lib/gtk/v3_10.nit b/lib/gtk/v3_10.nit
new file mode 100644 (file)
index 0000000..f47b46e
--- /dev/null
@@ -0,0 +1,167 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# GTK+ services added at version 3.10
+module v3_10 is pkgconfig("gtk+-3.0")
+
+import v3_8
+
+redef class GtkWindow
+       fun titlebar=(widget: GtkWidget) `{ gtk_window_set_titlebar(self, widget); `}
+end
+
+# A vertical container of `GtkListBoxRow`
+extern class GtkListBox `{ GtkListBox * `}
+       super GtkContainer
+
+       new `{ return (GtkListBox*)gtk_list_box_new(); `}
+
+       fun prepend(child: GtkWidget) `{ gtk_list_box_prepend(self, child); `}
+
+       fun insert(child: GtkWidget, position: Int) `{ gtk_list_box_insert(self, child, position); `}
+
+       fun selected_row: GtkListBoxRow `{ return gtk_list_box_get_selected_row(self); `}
+
+       fun row_at_index(index: Int): GtkListBoxRow`{ return gtk_list_box_get_row_at_index(self, index); `}
+
+       fun row_at_y(y: Int): GtkListBoxRow `{ return gtk_list_box_get_row_at_y(self, y); `}
+
+       fun select_row(row: GtkListBoxRow) `{ gtk_list_box_select_row(self, row); `}
+
+       fun placeholder=(placeholder: GtkWidget) `{ gtk_list_box_set_placeholder(self, placeholder); `}
+
+       fun adjustment=(adjustment: GtkAdjustment) `{ gtk_list_box_set_adjustment(self, adjustment); `}
+
+       fun selection_mode=(mode: GtkSelectionMode) `{ gtk_list_box_set_selection_mode(self, mode); `}
+
+       fun selection_mode: GtkSelectionMode `{ return gtk_list_box_get_selection_mode (self); `}
+
+       fun invalidate_filter `{ gtk_list_box_invalidate_filter(self); `}
+
+       fun invalidate_sort `{ gtk_list_box_invalidate_sort(self); `}
+
+       fun invalidate_headers `{ gtk_list_box_invalidate_headers(self); `}
+
+       fun activate_on_single_click=(single: Bool) `{ gtk_list_box_set_activate_on_single_click(self, single); `}
+
+       fun activate_on_single_click: Bool `{ return gtk_list_box_get_activate_on_single_click(self); `}
+
+       fun drag_unhighlight_row `{ gtk_list_box_drag_unhighlight_row(self); `}
+
+       fun drag_highlight_row(row: GtkListBoxRow) `{ gtk_list_box_drag_highlight_row(self, row); `}
+
+       # TODO
+       #fun (* GtkListBoxForeachFunc)(GtkListBoxRow *row, gpointer user_data) `{ (* GtkListBoxForeachFunc)(self, GtkListBoxRow *row, gpointer user_data); `}
+       #fun set_filter_func(GtkListBoxFilterFunc filter_func, gpointer user_data, GDestroyNotify destroy) `{ gtk_list_box_set_filter_func(self, GtkListBoxFilterFunc filter_func, gpointer user_data, GDestroyNotify destroy); `}
+       #fun set_header_func(GtkListBoxUpdateHeaderFunc update_header, gpointer user_data, GDestroyNotify destroy) `{ gtk_list_box_set_header_func(self, GtkListBoxUpdateHeaderFunc update_header, gpointer user_data, GDestroyNotify destroy); `}
+       #fun set_sort_func(GtkListBoxSortFunc sort_func, gpointer user_data, GDestroyNotify destroy) `{ gtk_list_box_set_sort_func(self, GtkListBoxSortFunc sort_func, gpointer user_data, GDestroyNotify destroy); `}
+end
+
+# A single row of a `GtkListBox`
+extern class GtkListBoxRow `{ GtkListBoxRow* `}
+       super GtkWidget
+
+       new `{ return (GtkListBoxRow*)gtk_list_box_row_new(); `}
+
+       fun header: GtkWidget `{ return gtk_list_box_row_get_header(self); `}
+
+       fun header=(header: GtkWidget) `{ gtk_list_box_row_set_header(self, header); `}
+
+       fun index: Int `{ return gtk_list_box_row_get_index(self); `}
+
+       fun changed `{ gtk_list_box_row_changed(self); `}
+end
+
+# Horizontal container with a title and subtitle
+extern class GtkHeaderBar `{ GtkHeaderBar* `}
+       super GtkContainer
+
+       new `{ return (GtkHeaderBar*)gtk_header_bar_new(); `}
+
+       fun title=(title: Text) do native_title = title.to_cstring
+       private fun native_title=(title: NativeString) `{ gtk_header_bar_set_title(self, title); `}
+
+       fun title: String do return native_title.to_s
+       private fun native_title: NativeString `{ return (gchar *)gtk_header_bar_get_title(self); `}
+
+       fun subtitle=(subtitle: Text) do native_subtitle = subtitle.to_cstring
+       fun native_subtitle=(subtitle: NativeString) `{ gtk_header_bar_set_subtitle(self, subtitle); `}
+
+       fun subtitle: String do return native_subtitle.to_s
+       fun native_subtitle: NativeString `{ return (gchar *)gtk_header_bar_get_subtitle(self); `}
+
+       fun custom_title=(title_widget: GtkWidget) `{ gtk_header_bar_set_custom_title(self, title_widget); `}
+
+       fun custom_title: GtkWidget `{ return gtk_header_bar_get_custom_title(self); `}
+
+       fun pack_start(child: GtkWidget) `{ gtk_header_bar_pack_start(self, child); `}
+
+       fun pack_end(child: GtkWidget) `{ gtk_header_bar_pack_end(self, child); `}
+
+       fun show_close_button=(setting: Bool) `{ gtk_header_bar_set_show_close_button(self, setting); `}
+
+       fun show_close_button: Bool `{ return gtk_header_bar_get_show_close_button(self); `}
+end
+
+# Container with a single child visible at a time
+extern class GtkStack `{ GtkStack * `}
+       super GtkContainer
+
+       new `{ return (GtkStack*)gtk_stack_new(); `}
+
+       fun stack_add(child: GtkWidget, name: String) do native_stack_add(child, name.to_cstring)
+       private fun native_stack_add(child: GtkWidget, name: NativeString) `{ gtk_stack_add_named(self, child, name); `}
+
+       fun add_titled(child: GtkWidget, name, title: NativeString) `{ gtk_stack_add_titled(self, child, name, title); `}
+       fun native_add_titled(child: GtkWidget, name, title: NativeString) `{ gtk_stack_add_titled(self, child, name, title); `}
+
+       fun visible_child=(child: GtkWidget) `{ gtk_stack_set_visible_child(self, child); `}
+
+       fun visible_child: GtkWidget `{ return gtk_stack_get_visible_child(self); `}
+
+       fun visible_child_name=(name: Text) do native_visible_child_name = name.to_cstring
+       fun native_visible_child_name=(name: NativeString) `{ gtk_stack_set_visible_child_name(self, name); `}
+
+       fun visible_child_name: Text do return native_visible_child_name.to_s
+       fun native_visible_child_name: NativeString `{ return (gchar *)gtk_stack_get_visible_child_name(self); `}
+
+       fun set_visible_child_full(name: Text, transition: GtkStackTransitionType) do native_set_visible_child_full(name.to_cstring, transition)
+       fun native_set_visible_child_full(name: NativeString, transition: GtkStackTransitionType) `{
+               gtk_stack_set_visible_child_full(self, name, transition);
+       `}
+
+       fun homogeneous=(homogeneous: Bool) `{ gtk_stack_set_homogeneous(self, homogeneous); `}
+
+       fun homogeneous: Bool `{ return gtk_stack_get_homogeneous(self); `}
+
+       fun transition_duration=(duration: Int) `{ gtk_stack_set_transition_duration(self, duration); `}
+
+       fun transition_duration: Int `{ return gtk_stack_get_transition_duration(self); `}
+
+       fun transition_type=(transition: GtkStackTransitionType) `{ gtk_stack_set_transition_type(self, transition); `}
+
+       fun transition_type: GtkStackTransitionType `{ return gtk_stack_get_transition_type(self); `}
+end
+
+# Type of animation used for transitions between pages in a `GtkStack`
+extern class GtkStackTransitionType `{ GtkStackTransitionType `}
+       new none `{ return GTK_STACK_TRANSITION_TYPE_NONE; `}
+       new crossfade `{ return GTK_STACK_TRANSITION_TYPE_CROSSFADE; `}
+       new slide_right `{ return GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT; `}
+       new slide_left `{ return GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT; `}
+       new slide_up `{ return GTK_STACK_TRANSITION_TYPE_SLIDE_UP; `}
+       new slide_down `{ return GTK_STACK_TRANSITION_TYPE_SLIDE_DOWN; `}
+       new slide_left_right `{ return GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT; `}
+       new slide_up_down `{ return GTK_STACK_TRANSITION_TYPE_SLIDE_UP_DOWN; `}
+end
diff --git a/lib/gtk/v3_4/gdk.nit b/lib/gtk/v3_4/gdk.nit
new file mode 100644 (file)
index 0000000..2195ee2
--- /dev/null
@@ -0,0 +1,43 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services from GDK
+module gdk is pkgconfig "gtk+-3.0"
+
+import gtk_core
+
+`{
+#ifdef GdkCallback_run
+       // Callback to GdkCallaback::run
+       gboolean nit_gdk_callback(gpointer user_data) {
+               GdkCallback_decr_ref(user_data);
+               return GdkCallback_run(user_data);
+       }
+#endif
+`}
+
+# Callback to pass to `gdk_threads_add_idle`
+class GdkCallback
+
+       # Small unit of code executed by the GDK loop when idle
+       #
+       # Returns true if this object should be invoked again.
+       fun run: Bool do return false
+end
+
+# Add a callback to execute whenever there are no higher priority events pending
+fun gdk_threads_add_idle(callback: GdkCallback): Int import GdkCallback.run `{
+       GdkCallback_incr_ref(callback);
+       return gdk_threads_add_idle(&nit_gdk_callback, callback);
+`}
index 34319dc..0d43784 100644 (file)
@@ -60,7 +60,7 @@ extern class GtkAssistant `{GtkAssistant *`}
                return gtk_assistant_insert_page(self, page, position);
        `}
 
-       fun remove(page_num: Int) `{
+       fun remove_page(page_num: Int) `{
                gtk_assistant_remove_page(self, page_num);
        `}
 
index b9929f5..f7d2c78 100644 (file)
@@ -43,13 +43,19 @@ in "C Header" `{
 `}
 
 # Initialize the GTK system
-fun init_gtk `{ gtk_init(0, NULL); `}
+fun gtk_init `{ gtk_init(0, NULL); `}
 
 # Hand over control to the GTK event loop
-fun run_gtk `{ gtk_main(); `}
+fun gtk_main `{ gtk_main(); `}
+
+# Run a single iteration of the main loop, block until an event is noticed
+fun gtk_main_iteration: Bool `{ return gtk_main_iteration(); `}
+
+# Run a single iteration of the main loop, only block until an event is noticed if `blocking`
+fun gtk_main_iteration_do(blocking: Bool): Bool `{ return gtk_main_iteration_do(blocking); `}
 
 # Quit the GTK event loop and clean up the system
-fun quit_gtk `{ gtk_main_quit(); `}
+fun gtk_main_quit `{ gtk_main_quit(); `}
 
 interface GtkCallable
        # return true to stop event processing, false to let it propagate
@@ -142,8 +148,9 @@ extern class GtkContainer `{GtkContainer *`}
        fun add(widget: GtkWidget) `{
                gtk_container_add(self, widget);
        `}
+
        # Remove the widget from the container
-       fun remove_widget(widget: GtkWidget) `{
+       fun remove(widget: GtkWidget) `{
                gtk_container_remove(self, widget);
        `}
 
@@ -156,7 +163,6 @@ extern class GtkContainer `{GtkContainer *`}
        fun resize_mode=(resize_mode: GtkResizeMode) `{
                gtk_container_set_resize_mode(self, resize_mode);
        `}
-
 end
 
 # A container with just one child
@@ -290,6 +296,9 @@ extern class GtkWindow `{GtkWindow *`}
        fun keep_below=(setting: Bool) `{
                gtk_window_set_keep_below(self, setting);
        `}
+
+       # Try to convince the window manage to decorate or not this window
+       fun decorated=(setting: Bool) `{ gtk_window_set_decorated(self, setting); `}
 end
 
 # A bin with a decorative frame and optional label
@@ -772,6 +781,15 @@ extern class GtkButton `{GtkButton *`}
                        signal_connect("clicked", to_call, user_data)
        end
 
+       # Set the image of button to the given widget
+       fun image=(image: GtkWidget) `{
+               gtk_button_set_image(self, image);
+       `}
+
+       # Get the widget that is currenty set as the image of button
+       fun image: GtkWidget `{
+               return gtk_button_get_image(self);
+       `}
 end
 
 # A button which pops up a scale
@@ -1054,4 +1072,3 @@ end
 
 extern class GdkRGBA `{GdkRGBA*`}
 end
-
index db45724..bd1d63b 100644 (file)
@@ -20,3 +20,4 @@ module v3_4 is pkgconfig "gtk+-3.0"
 import gtk_widgets_ext
 import gtk_dialogs
 import gtk_assistant
+import gdk
index 4f67eed..f3deb77 100644 (file)
@@ -21,7 +21,7 @@ import gtk
 import data_store
 
 redef class App
-       redef fun setup do init_gtk
+       redef fun setup do gtk_init
 
        # On GNU/Linux, we go through all the callbacks once,
        # there is no complex life-cycle.
@@ -34,7 +34,7 @@ redef class App
 
                var window = window
                window.native.show_all
-               run_gtk
+               gtk_main
 
                app.on_pause
                app.on_stop
index 4916f4b..28ee606 100644 (file)
@@ -33,19 +33,18 @@ class MyApp
 
                if sender == but_ok then
                        print "ok!"
-                       quit_gtk
+                       gtk_main_quit
                else if sender == but_cancel then
                        print "cancel!"
-                       quit_gtk
+                       gtk_main_quit
                else
                        print sender
                end
-               
        end
 
        init
        do
-               init_gtk
+               gtk_init
 
                win = new GtkWindow(new GtkWindowType.toplevel)
                win.connect_destroy_signal_to_quit
@@ -70,5 +69,5 @@ end
 
 if "NIT_TESTING".environ != "true" then
        var app = new MyApp
-       run_gtk
+       gtk_main
 end