Unable to open a GtkWindow from a cinnamon applet - applet

When I try to open a GtkWindow from a cinnamon applet, the entire desktop freezes.
No errors in the ~/.cinnamon/glass.log file.
const Gtk = imports.gi.Gtk;
function MyApplet(orientation)
{
this._init(orientation);
}
MyApplet.prototype =
{
__proto__: Applet.IconApplet.prototype,
_init: function(orientation)
{
Applet.IconApplet.prototype._init.call(this, orientation);
try {
this.set_applet_icon_name("dialog-question");
this.set_applet_tooltip("test");
}
catch (e) {
global.logError(e);
};
},
on_applet_clicked: function(event)
{
Gtk.init(null, 0);
let mwindow = new Gtk.Window ({type : Gtk.WindowType.TOPLEVEL});
mwindow.title = "Hello World!";
mwindow.connect ("destroy", function(){Gtk.main_quit()});
mwindow.show();
Gtk.main();
}
};
function main(metadata, orientation)
{
let myApplet = new MyApplet(orientation);
return myApplet;
}
The code is executed until Gtk.main() then no window is displayed and the desktop get frozen.
Anyone knows how to make it work correctly?

Javascript can't do multithreading, that's why calling Gtk.main(); breaks Cinnamon.
Cinnamon applet already runs a main loop and the call of Gtk.main(); tries to create another one.
So it's not possible to open a GtkWindow from a cinnamon applet directly in Javascript.
The solution could be to open a GtkWindow through a Python script, and to use DBus to communicate between the Cinnamon applet and the Python/GTK window.
Opened issue in Cinnamon GitHub

This is how you can do it:
const Gtk = imports.gi.Gtk;
const Util = imports.misc.util;
function MyApplet(orientation)
{
this._init(orientation);
}
MyApplet.prototype =
{
__proto__: Applet.IconApplet.prototype,
_init: function(orientation)
{
Applet.IconApplet.prototype._init.call(this, orientation);
try {
this.set_applet_icon_name("dialog-question");
this.set_applet_tooltip("test");
}
catch (e) {
global.logError(e);
};
},
on_applet_clicked: function(event)
{
//path to your applet directory; hardcoded for now!
let path="~/.local/share/cinnamon/applets/your_applet#you.org";
//create in your applet directory a file "yourgtkfile.js" and
//make it executable "chmod +x yourgtkfile.js"
Util.spawnCommandLine(path + "/yourgtkfile.js");
}
};
function main(metadata, orientation)
{
let myApplet = new MyApplet(orientation);
return myApplet;
}
You can copy/paste this in yourgtkfile.js. (Change #!/usr/bin/gjs with #!/usr/bin/cjs)
Or, this one (taken from here) (Change #!/usr/bin/gjs with #!/usr/bin/cjs):
#!/usr/bin/cjs
const Lang = imports.lang;
const Gtk = imports.gi.Gtk;
const Application = new Lang.Class({
//A Class requires an explicit Name parameter. This is the Class Name.
Name: 'Application',
//create the application
_init: function() {
this.application = new Gtk.Application();
//connect to 'activate' and 'startup' signals to handlers.
this.application.connect('activate', Lang.bind(this, this._onActivate));
this.application.connect('startup', Lang.bind(this, this._onStartup));
},
//create the UI
_buildUI: function() {
this._window = new Gtk.ApplicationWindow({ application: this.application,
title: "Hello World!" });
this._window.set_default_size(200, 200);
this.label = new Gtk.Label({ label: "Hello World" });
this._window.add(this.label);
},
//handler for 'activate' signal
_onActivate: function() {
//show the window and all child widgets
this._window.show_all();
},
//handler for 'startup' signal
_onStartup: function() {
this._buildUI();
}
});
//run the application
let app = new Application();
app.application.run(ARGV);
I supposed that you don't need to communicate with the app just launched :)

Related

Image not displaying in .NET MAUI Mac Catalyst

I'm trying to display an image to make my own custom splash screen using .NET MAUI. It is made in C# and does not use XAML. Here is my code:
SplashScreenActivity.cs:
using System;
using System.Text;
namespace LiveEditorHTML
{
public partial class SplashScreenActivity : ContentPage
{
Image splashScreenImage;
public async Task<string> ShowMsg(string title,
string msg, bool isQuestion, bool isInput,
int? num, string[]? question)
{
bool answer;
if (isQuestion && !isInput)
{
answer = await DisplayAlert(title, msg, "Yes", "No");
return answer.ToString();
}
else if (!isQuestion && !isInput)
{
await DisplayAlert(title, msg, "OK");
}
else if (!isQuestion && isInput)
{
string action = await DisplayActionSheet(
title + msg, "Cancel",
null, question
);
}
else
{
await DisplayAlert(title, msg, "OK");
}
return null;
}
public SplashScreenActivity()
{
var uiView = new ScrollView();
var stackLayout = new VerticalStackLayout();
var img = AssetsHelper.LoadMauiAsset("logo.png").Result;
Task.Run(() =>
{
string output = ShowMsg("Debug", img, false, false, 0, null).Result;
});
byte[] byteArray = Encoding.UTF8.GetBytes(img);
MemoryStream stream = new MemoryStream(byteArray);
splashScreenImage = new Image()
{
Source = ImageSource.FromStream(() => stream)
};
stackLayout.Children.Add(splashScreenImage);
this.Content = uiView;
}
}
}
AssetsHelper.cs:
using System;
namespace LiveEditorHTML
{
public class AssetsHelper
{
public static async Task<string> LoadMauiAsset(string fileName)
{
using var stream = await FileSystem.OpenAppPackageFileAsync(fileName);
using var reader = new StreamReader(stream);
var contents = reader.ReadToEndAsync();
return contents.Result;
}
}
}
Here is the image I used, created with GIMP, the image size is 456x456 (the same as the image sizes of the appicon.svg and appiconfg.svg files located at: Resources\AppIcon):
The ShowMsg() function is used to create a MessageBox like C# Winforms, in addition, it is also used for the cases of creating a Yes No questionnaire, and creating a questionnaire that requires the user to enter text. Currently, I just use the simplest feature, like the MessageBox in C# Winforms, which is to print a debug message, with the title Debug and the content as an image file that is read with the help of the AssetsHelper.cs support class.
When I run the program, the Debug window for printing information pops up, indicating that it is working, the file logo.png (with path at Resources\Raw) has been read successfully:
But then nothing is displayed:
I highly suspected that there might be an error, but no exceptions occurred, so I used the built-in image: dotnet_bot.svg to test (link at: Resources\Images). I replaced the following line in SplashScreenActivity.cs:
ImageSource.FromStream(() => stream)
Fort:
"dotnet_bot.svg"
The code becomes:
splashScreenImage = new Image()
{
Source = "dotnet_bot.svg"
};
to test. When I turn on the app and go through the Debug screen (since I haven't dropped the code that shows the Debug dialog), they don't work either:
And no exception is thrown. The app doesn't crash either.
All versions of .NET MAUI and VS are updated and VS is the latest Preview version. The computer I'm using is a Macbook Pro running macOS Monterey 12.5.1
So what's going on?
I had create a sample to test your code and the image can't show either. I found that you have changed the image file to the string, and then changed the string to the byte array.
You can try to convert the image to the byte array or set the image as the source directly. In addition, you didn't add the stacklayout into the scrollview. So you should add it or set the stacklayout as the page.content.
Set the image as the source directly
splashScreenImage = new Image()
{
Source = "test" // the test.png is in the /Resource/Image folder
};
stackLayout.Children.Add(splashScreenImage);
this.Content = stackLayout;
2.Convert the image to the byte array directly
public static async Task<byte[]> LoadMauiAsset(string fileName)
{
using var stream = await FileSystem.OpenAppPackageFileAsync(fileName);
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
return buffer;
}
MemoryStream stream = new MemoryStream(AssetsHelper.LoadMauiAsset("test").Result);
splashScreenImage = new Image()
{
Source = ImageSource.FromStream(() => stream)
};
stackLayout.Children.Add(splashScreenImage);
this.Content = stackLayout;

GTK FileChooserDialog select files AND folders (Vala)

Is there any way to make the FileChooserDialog to select both files and folders?
I know there are the FileChooserAction OPEN and SELECT_FOLDER but they are exclusive.
PD: I dont't want two buttons, I already know how to do this. What I want is to get the routes of all selected elements (both files and folders) with the same button.
The File chooser action is different from what you want. I think you are after the set_select_multiple () method or the select_multiple property (both inherited from the Gtk.FileChooser interface).
Then you can use the methods get_filenames () or get_uris (), depending on your needs.
The default GtkFileChooserDialog only allows you to select folders and files if you are on the Recent "tab" but as soon as you use a normal folder it won't let you do that.
In order to achieve that you must use Gtk.FileChooserWidget by composing a solution or creating a new widget (eg. subclassing Gtk.FileChooserWidget or Gtk.Dialog).
I've created a simple example that will work as you want and that you can easily change to suit your needs.
The following code is based on Valadoc.org Gtk.FileChooserWidget page, which does what you are asking:
public class Application : Gtk.Window {
public Application () {
// Prepare Gtk.Window:
this.window_position = Gtk.WindowPosition.CENTER;
this.destroy.connect (Gtk.main_quit);
// VBox:
Gtk.Box vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5);
this.add (vbox);
// HeaderBar:
Gtk.HeaderBar hbar = new Gtk.HeaderBar ();
hbar.set_title ("MyFileChooser");
hbar.set_subtitle ("Select Files and Folders");
// HeaderBar Buttons
Gtk.Button cancel = new Gtk.Button.with_label ("Cancel");
Gtk.Button select = new Gtk.Button.with_label ("Select");
hbar.pack_start (cancel);
hbar.pack_end (select);
this.set_titlebar (hbar);
// Add a chooser:
Gtk.FileChooserWidget chooser = new Gtk.FileChooserWidget (Gtk.FileChooserAction.OPEN);
vbox.pack_start (chooser, true, true, 0);
// Multiple files can be selected:
chooser.select_multiple = true;
// Add a preview widget:
Gtk.Image preview_area = new Gtk.Image ();
chooser.set_preview_widget (preview_area);
chooser.update_preview.connect (() => {
string uri = chooser.get_preview_uri ();
// We only display local files:
if (uri.has_prefix ("file://") == true) {
try {
Gdk.Pixbuf pixbuf = new Gdk.Pixbuf.from_file (uri.substring (7));
Gdk.Pixbuf scaled = pixbuf.scale_simple (150, 150, Gdk.InterpType.BILINEAR);
preview_area.set_from_pixbuf (scaled);
preview_area.show ();
} catch (Error e) {
preview_area.hide ();
}
} else {
preview_area.hide ();
}
});
// HBox:
Gtk.Box hbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5);
vbox.pack_start(hbox, false, false, 0);
// Setup buttons callbacks
cancel.clicked.connect (() => {
this.destroy ();
});
select.clicked.connect (() => {
SList<string> uris = chooser.get_uris ();
foreach (unowned string uri in uris) {
stdout.printf (" %s\n", uri);
}
this.destroy ();
});
}
public static int main (string[] args) {
Gtk.init (ref args);
Application app = new Application ();
app.show_all ();
Gtk.main ();
return 0;
}
}
Compile with:
valac --pkg gtk+-3.0 Gtk.FileChooserDialog.vala
After you choose select, the application will print your selection to the console:
Dumps (path partially replaced with ...):
file:///.../stackoverflow/3305/1
file:///.../stackoverflow/3305/2
file:///.../stackoverflow/3305/3
file:///.../stackoverflow/3305/Gtk.FileChooserDialog
file:///.../stackoverflow/3305/Gtk.FileChooserDialog.vala
file:///.../stackoverflow/3305/Gtk.FileChooserWidget
file:///.../stackoverflow/3305/Gtk.FileChooserWidget.vala
file:///.../stackoverflow/3305/img1.jpg
file:///.../stackoverflow/3305/img2.jpg
file:///.../stackoverflow/3305/img3.jpg
file:///.../stackoverflow/3305/Makefile

Window Only Renders correctly on first show

I have a window that I am opening like so
if (Window == null) {
var con = WindowType.GetConstructor(new Type[0]);
Window = (PopupWindow)con.Invoke(new object[0]);
//The types are subclasses of PopupWindow.
Window.Controller = this;
Window.Show ();
}
This correctly displays the window as long as it is the first of these windows to pop up... If I close the window and create an entirely new one, the window is just a grey area until I restart debugging... Any ideas?
public PopupWindow () : base(Gtk.WindowType.Toplevel)
{
this.AppPaintable = true;
this.Colormap = this.Screen.RgbaColormap;
this.Events = Gdk.EventMask.AllEventsMask;
this.Decorated = false;
this.SkipTaskbarHint = true;
}
Example subclass
public StorageWindow () : base()
{
this.Build ();
this.Move (this.Screen.Width - 428, 55);
//set some label props.
StorageCircle.ExposeEvent += (o, args) => {
//Draw a circle
};
}
P.S. This is how I am destroying the window.
if (Window != null) {
Window.Destroy();
Window = null;
}
The entire issue turned out to be caused by a non gtk timer trying to edit widgets outside of the main thread.

JavaFx Showing popup is not working on client machine

Following is my code to show popup windows in my JavaFx Desktop Application.
public boolean popup(Object parent, ViewModelBase viewModel, AsyncCommand cancelCommand) {
javafx.scene.layout.Pane root = null;
try
{
if(!IOC.platformInfo.isPlatformThread())
{
return PlatformUtil.runAndWait(()->
{
return popup(parent,viewModel,cancelCommand);
});
}
String name = viewModel.getClass().getSimpleName();
name = Pattern.compile("(viewModel|Controller)$",Pattern.CASE_INSENSITIVE)
.matcher(name).replaceAll("View");
FXMLLoader loader = new FXMLLoader(com.technoinn.videoprospector.ui.fx.service.WindowService
.class.getResource(String.format("/fxml/%s.fxml",name))
, null, new JavaFXBuilderFactory(), new IocControllerFactory());
if(viewModel instanceof ControllerBase)
{
loader.setController(viewModel);
}
root = loader.load();
if(!(viewModel instanceof ControllerBase))
{
Object controller = loader.getController();
if(controller instanceof ControllerBase)
{
((ControllerBase)controller).setViewModel(viewModel);
}
}
jfxtras.scene.control.window.Window window =
new jfxtras.scene.control.window.Window(viewModel.getDisplayName());
window.getContentPane().getChildren().add(root);
window.setPrefSize(root.getPrefWidth(), root.getPrefHeight());
window.setMinSize(root.getPrefWidth(), root.getPrefHeight());
CloseIcon closeIcon = new CloseIcon(window);
window.getLeftIcons().add(closeIcon);
Scene scene = new Scene(window);
// Scene scene = new Scene(root);
scene.getStylesheets().add(FxModule.StyleFile);
Stage stage = new Stage(StageStyle.UNDECORATED);
stage.setResizable(true);
stage.setMinWidth(root.getPrefWidth());
stage.setMinHeight(root.getPrefHeight());
viewModel.addPropertyChangeListener(ViewModelBase.closeCommand,
(x)->
{
if(x.getNewValue()!=null && x.getNewValue()==Boolean.TRUE)
{
stage.close();
}
});
closeIcon.setCursor(Cursor.HAND);
closeIcon.setOnAction((x)->
{
if(cancelCommand!=null)
cancelCommand.beginExecution();
else
stage.close();
});
/*
stage.setOnCloseRequest((WindowEvent event) -> {
if(cancelCommand!=null)
cancelCommand.beginExecution();
else
stage.close();
});*/
stage.setScene(scene);
stage.centerOnScreen();
stage.initModality(Modality.APPLICATION_MODAL);
if(!parentWindows.isEmpty())
{
stage.initOwner(parentWindows.peek());
stage.initModality(Modality.WINDOW_MODAL);
}
parentWindows.push(stage);
stage.showAndWait();
parentWindows.pop();
}catch(Exception exp)
{
Logger.getGlobal().log(Level.SEVERE,"Error in popup",exp);
}
return true;
}
Problem is, popup shows well and in proper size on my machine.(Dev Machine). But size on target client machine is unpredictable. Sometimes it is very small and sometimes it does not even show the content pane of the popup window. Client machine has jRE 1.8_31. Any idea what can be wrong. Client machine size is same as that of my dev machine.
Thanks
Most probably you are calling next code too soon:
window.setPrefSize(root.getPrefWidth(), root.getPrefHeight());
window.setMinSize(root.getPrefWidth(), root.getPrefHeight());
and
stage.setMinWidth(root.getPrefWidth());
stage.setMinHeight(root.getPrefHeight());
Most layout values are being calculated only after scene is shown. Try to move such code after call to
stage.showAndWait();

How to integrate hockey App with Hybrid mobile app

I am trying to integrate my Hybrid Mobile App (Inonic + Cordova) with hockey App
but the problem is Hockey App is support Native apps (According to my info). So is there any guide available for that?
Hybrid App integration with Hockey app.
When I try to follow hockey app integration with android platform (hybrid app) it also said me to add some code in main activity so where i can find this
Main activity is inside Android platform... cordova/platforms/android/src/...
Put in onCreate method the Register...
There also some plugins for help in this task like https://github.com/peutetre/cordova-plugin-hockeyapp
Take into account that a lot of crash JavaScript problems do not crash in native world it would be helpful to use additional way to communicate controlled errors for example the saveException method, try to expose this by plugin into javascript, it will let store context information error: http://hockeyapp.net/help/sdk/android/3.0.1/net/hockeyapp/android/ExceptionHandler.html
I have tested the solution for Android only in a fork of the previous mentioned plugin:
https://github.com/m-alcu/cordova-plugin-hockeyapp
There are several actions available but yo only need to use "start" and "saveException" for controlled errors to be send to hockeyapps.
hockeyapp.js:
var exec = require('cordova/exec');
var hockeyapp = {
start:function(success, failure, token) {
exec(success, failure, "HockeyApp", "start", [ token ]);
},
feedback:function(success, failure) {
exec(success, failure, "HockeyApp", "feedback", []);
},
saveException:function(success, failure, description) {
exec(success, failure, "HockeyApp", "saveException", [ description ]);
}
};
module.exports = hockeyapp;
hockeyapp.java:
package com.zengularity.cordova.hockeyapp;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import android.widget.Toast;
import static net.hockeyapp.android.ExceptionHandler.saveException;
import net.hockeyapp.android.FeedbackManager;
import net.hockeyapp.android.CrashManager;
import net.hockeyapp.android.CrashManagerListener;
public class HockeyApp extends CordovaPlugin {
public static boolean initialized = false;
public static String token;
public static String description;
#Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
if (action.equals("start")) {
token = args.optString(0);
CrashManager.register(cordova.getActivity(), token, null);
initialized = true;
callbackContext.success();
return true;
} else if(action.equals("feedback")) {
token = args.optString(0);
FeedbackManager.register(cordova.getActivity(), token, null);
cordova.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
FeedbackManager.showFeedbackActivity(cordova.getActivity());
}
});
callbackContext.success();
return true;
} else if(action.equals("saveException")) {
description = args.optString(0);
if(initialized) {
Toast toast = Toast.makeText(cordova.getActivity(), "problem", Toast.LENGTH_SHORT);
toast.show();
cordova.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Exception e = new Exception("Send problem");
saveException(e, new CrashManagerListener() {
public String getDescription() {
return description;
}
});
}
});
callbackContext.success();
return true;
} else {
callbackContext.error("cordova hockeyapp plugin not initialized, call start() first");
return false;
}
}
else {
return false;
}
}
}
example of use this plugin in a hellowold example (index.js):
var app = {
// Application Constructor
initialize: function() {
this.bindEvents();
},
// Bind Event Listeners
//
// Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);
},
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicitly call 'app.receivedEvent(...);'
onDeviceReady: function() {
app.receivedEvent('deviceready');
},
// Update DOM on a Received Event
receivedEvent: function(id) {
var parentElement = document.getElementById(id);
var listeningElement = parentElement.querySelector('.listening');
var receivedElement = parentElement.querySelector('.received');
listeningElement.setAttribute('style', 'display:none;');
receivedElement.setAttribute('style', 'display:block;');
console.log('Received Event: ' + id);
hockeyapp.start(
function() { alert('hockeyapp initialised'); },
function(msg) { alert(msg); },
'< your APP ID >');
hockeyapp.saveException(
function() { alert('hockeyapp saveException'); },
function(msg) { alert(msg); },
'Something wrong has happened: bla bla bla...');
}
};
app.initialize();
Hockey stores these controlled exceptions in the file directory of the app and asks to send it the next time user opens app: