From 1ec01061e26b5780396a3fa895c1796dcafed346 Mon Sep 17 00:00:00 2001 From: Cecil Date: Fri, 4 Aug 2017 22:41:15 -0600 Subject: [PATCH] weaponize #363 - systray * type check the args * add some Ruby garbage collection thinking. * update the manual * needs osx --- Tests/systray/note.rb | 8 +++++- shoes/native/gtk.c | 8 +++--- shoes/native/gtk/gtksystray.c | 7 +++++ shoes/types/systray.c | 39 +++++++++++++++++++++------- static/manual-en.txt | 49 +++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 15 deletions(-) diff --git a/Tests/systray/note.rb b/Tests/systray/note.rb index 3337cd37..eb776503 100644 --- a/Tests/systray/note.rb +++ b/Tests/systray/note.rb @@ -4,7 +4,13 @@ ctr = 0; button "Notify" do ctr += 1 - systray title: "Notify Test", message: "message ##{ctr}", icon: "#{DIR}/static/shoes-icon.png" + icp = '' + if ctr % 3 != 0 + icp = "#{DIR}/static/shoes-icon.png" + else + icp = "#{DIR}/static/shoes-icon-red.png" + end + systray title: "Notify Test", message: "message ##{ctr}", icon: icp end end end diff --git a/shoes/native/gtk.c b/shoes/native/gtk.c index 3da2e5cb..3bef2385 100644 --- a/shoes/native/gtk.c +++ b/shoes/native/gtk.c @@ -89,7 +89,7 @@ GtkApplication *shoes_GtkApp; void shoes_gtk_app_activate (GApplication *app, gpointer user_data) { shoes_GApp = app; - fprintf(stderr, "shoes_gtk_app_activate called\n"); + //fprintf(stderr, "shoes_gtk_app_activate called\n"); //GtkWidget *widget; //widget = gtk_application_window_new (GTK_APPLICATION (app)); //gtk_widget_show (widget); @@ -100,16 +100,16 @@ void shoes_native_init() { curl_global_init(CURL_GLOBAL_ALL); #endif #ifndef SHOES_GTK_WIN32 - /* try using gtk_application_new() instead of gtk_init + /* try using linux gtk_application_new() instead of gtk_init */ int status; char *empty = NULL; - shoes_GtkApp = gtk_application_new ("org.gnome.example", G_APPLICATION_FLAGS_NONE); + shoes_GtkApp = gtk_application_new ("com.mvmanila.shoes", G_APPLICATION_FLAGS_NONE); shoes_GApp = (G_APPLICATION (shoes_GtkApp)); g_signal_connect (shoes_GtkApp, "activate", G_CALLBACK (shoes_gtk_app_activate), NULL); status = g_application_run (G_APPLICATION (shoes_GtkApp), 0, &empty); - //gtk_init(NULL, NULL); #else + // windows Shoes <= 3.3.3 gtk_init(NULL, NULL); #endif } diff --git a/shoes/native/gtk/gtksystray.c b/shoes/native/gtk/gtksystray.c index 384f7bde..3b078975 100644 --- a/shoes/native/gtk/gtksystray.c +++ b/shoes/native/gtk/gtksystray.c @@ -22,9 +22,16 @@ void shoes_native_systray(char *title, char *message, char *path) { #else // try older gtk_status_icon for Windows to see what happens static GtkStatusIcon *stsicon = NULL; +static char *stspath = NULL; void shoes_native_systray(char *title, char *message, char *path) { if (stsicon == NULL) { stsicon = gtk_status_icon_new_from_file(path); + stspath = path; + } + // detect change of icon + if (strcmp(path, stspath)) { + stspath = path; + gtk_status_icon_set_from_file (stsicon, stspath); } gtk_status_icon_set_title(stsicon, title); gtk_status_icon_set_tooltip_text(stsicon, message); diff --git a/shoes/types/systray.c b/shoes/types/systray.c index f7a2da0f..0f7ec206 100644 --- a/shoes/types/systray.c +++ b/shoes/types/systray.c @@ -1,4 +1,5 @@ /* systray + * Is not really a widget with things you can modifiy */ #include "shoes/types/native.h" #include "shoes/types/systray.h" @@ -11,8 +12,7 @@ FUNC_M("+systray", systray, -1); shoes_systray_init() { cSystray = rb_define_class_under(cTypes, "Systray", cNative); rb_define_alloc_func(cSystray, shoes_systray_alloc); - //rb_define_method(cSystray, "width", CASTHOOK(shoes_svghandle_get_width), 0); - //rb_define_method(cSystray, "height", CASTHOOK(shoes_svghandle_get_height), 0); + // no methods RUBY_M("+systray", systray, -1); } @@ -48,24 +48,43 @@ VALUE shoes_systray_alloc(VALUE klass) { VALUE shoes_systray_new(int argc, VALUE *argv, VALUE parent) { // Get Ruby args. VALUE rbtitle, rbmessage, rbpath; - rbtitle = shoes_hash_get(argv[0], rb_intern("title")); - rbmessage = shoes_hash_get(argv[0], rb_intern("message")); - rbpath = shoes_hash_get(argv[0], rb_intern("icon")); + if (argc == 1) { + rbtitle = shoes_hash_get(argv[0], rb_intern("title")); + rbmessage = shoes_hash_get(argv[0], rb_intern("message")); + rbpath = shoes_hash_get(argv[0], rb_intern("icon")); + } else if (argc == 3) { + rbtitle = argv[0]; + rbmessage = argv[1]; + rbpath = argv[2]; + } else { + rb_raise(rb_eArgError, "Missing an argument to systray"); + } char *title = NULL, *message = NULL, *path = NULL; - // Alloc the object and init + /* Alloc the object and init. We do keep a copy of the strings + * which will be garbage collected in at some point by Ruby + * Assumes the strings and pixbugs in the native are copied + * out of our process memory into the Desktop's space. + */ VALUE obj = shoes_systray_alloc(cSystray); shoes_systray *self_t; Data_Get_Struct(obj, shoes_systray, self_t); + Check_Type(rbtitle, T_STRING); if ((!NIL_P(rbtitle)) && (RSTRING_LEN(rbtitle) > 0)) { - title = strdup(RSTRING_PTR(rbtitle)); + title = self_t->title = strdup(RSTRING_PTR(rbtitle)); } + Check_Type(rbmessage, T_STRING); if ((!NIL_P(rbmessage)) && (RSTRING_LEN(rbmessage) > 0)) { - message = strdup(RSTRING_PTR(rbmessage)); + message = self_t->message = strdup(RSTRING_PTR(rbmessage)); } + Check_Type(rbpath, T_STRING); if ((!NIL_P(rbpath)) && (RSTRING_LEN(rbpath) > 0)) { - path = strdup(RSTRING_PTR(rbpath)); + path = self_t->icon_path =strdup(RSTRING_PTR(rbpath)); + } + if (path == NULL || message == NULL || title == NULL) { + rb_raise(rb_eArgError, "Bad arguments to systray"); } // call the native widget - shoes_native_systray(title, message, path); // temporary + shoes_native_systray(title, message, path); + return Qnil; } diff --git a/static/manual-en.txt b/static/manual-en.txt index f188a318..8516b2e7 100644 --- a/static/manual-en.txt +++ b/static/manual-en.txt @@ -4423,6 +4423,55 @@ end note that the method returns the drawing code (text) of the snapshot, so you might take further control about details of your svg image for example. +== Systray == + +Systray allows you to send an icon and a text message to the desktop manager +which will be shown where the platform decides it should be (systray for Windows), +Notification area for Linux and OSX. Typically, you would call it for some +long running process, like a email client that got a new message and you the desktop +to know about it. + +This is not a widget you can modify. There are no methods other than creating it +and that will return nil (even when it works). There is no guarantee it will work, +particularly if you call it too often and expect everything to show up. That's up to +the OS and Desktop manager, not Shoes. + +You can call it in two ways, with positional arguments or with a hash of arguments. + + +=== systray , , » nil === + +The first string is the title, the second is the message to display and the +third string is a file path to the icon. The example below describes the preferred +way + +=== systray title: , message: , icon: » nil === + +You can also use a hash of arguments. The example below is worth some study. +{{{ +#!ruby +Shoes.app do + stack do + para "Press button and look in your systems notication area" + ctr = 0; + button "Notify" do + ctr += 1 + icp = '' + if ctr % 3 != 0 + icp = "#{DIR}/static/shoes-icon.png" + else + icp = "#{DIR}/static/shoes-icon-red.png" + end + systray title: "Shoes Notify", message: "message ##{ctr}", icon: icp + end + end +end +}}} + +Notice how we change the message: and every third click, the icon changes. +Changes to the title: may not appear, depending on your platform so you +not change change it. All three arguments must be included. + == TextBlock == The TextBlock object represents a group of text organized as a single element.