From: Jean Privat Date: Thu, 5 Nov 2015 15:13:53 +0000 (-0500) Subject: Merge: Intro `Task` and use in for Android's `run_on_ui_thread` X-Git-Tag: v0.8~107 X-Git-Url: http://nitlanguage.org?hp=6bbbd860a81f70cb15cd110c363d8a313e7a26e4 Merge: Intro `Task` and use in for Android's `run_on_ui_thread` 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 Reviewed-by: Romain Chanoir --- diff --git a/VERSION b/VERSION index 151a686..a502cc6 100644 --- 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 index 0000000..f47b46e --- /dev/null +++ b/lib/gtk/v3_10.nit @@ -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 index 0000000..2195ee2 --- /dev/null +++ b/lib/gtk/v3_4/gdk.nit @@ -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); +`} diff --git a/lib/gtk/v3_4/gtk_assistant.nit b/lib/gtk/v3_4/gtk_assistant.nit index 34319dc..0d43784 100644 --- a/lib/gtk/v3_4/gtk_assistant.nit +++ b/lib/gtk/v3_4/gtk_assistant.nit @@ -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); `} diff --git a/lib/gtk/v3_4/gtk_core.nit b/lib/gtk/v3_4/gtk_core.nit index b9929f5..f7d2c78 100644 --- a/lib/gtk/v3_4/gtk_core.nit +++ b/lib/gtk/v3_4/gtk_core.nit @@ -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 - diff --git a/lib/gtk/v3_4/v3_4.nit b/lib/gtk/v3_4/v3_4.nit index db45724..bd1d63b 100644 --- a/lib/gtk/v3_4/v3_4.nit +++ b/lib/gtk/v3_4/v3_4.nit @@ -20,3 +20,4 @@ module v3_4 is pkgconfig "gtk+-3.0" import gtk_widgets_ext import gtk_dialogs import gtk_assistant +import gdk diff --git a/lib/linux/ui.nit b/lib/linux/ui.nit index 4f67eed..f3deb77 100644 --- a/lib/linux/ui.nit +++ b/lib/linux/ui.nit @@ -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 diff --git a/tests/test_gtk.nit b/tests/test_gtk.nit index 4916f4b..28ee606 100644 --- a/tests/test_gtk.nit +++ b/tests/test_gtk.nit @@ -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