Why GtkWidget is not being found - gtk

I have following simple application which creates a window with Label, Entry and Button:
using Gtk;
public static int main(string[] args) {
Gtk.init(ref args);
var mywin = new MyWindow("Entrypad");
mywin.show_all();
Gtk.main();
return 0;
}
public class MyWindow : Window{
public MyWindow(string stitle) {
this.title = stitle;
this.destroy.connect(Gtk.main_quit);
var grid = new Grid();
this.add(grid);
var lab = new Label("Mylabel:");
lab.set_xalign(1);
grid.attach(lab, 0, 0, 1, 1 );
Entry ent = new Entry();
grid.attach(ent, 1, 0, 1, 1 );
var printButton = new Button.with_label("Print Button");
grid.attach(printButton, 0, 1, 1, 1);
printButton.clicked.connect( printButtonFn );
}
}
private void printButtonFn(){
print("In print fn; \n");
}
Above code compiles and works all right. However, I want to show a dialog box, hence I modify printButtonFn and add code taken from here:
private void printButtonFn(){
print("In print fn; \n");
quick_message(this, "Message for dialog box.");
}
// Function to open a dialog box with a message
void quick_message (GtkWindow *parent, gchar *message) {
GtkWidget *dialog, *label, *content_area;
GtkDialogFlags flags;
flags = GTK_DIALOG_DESTROY_WITH_PARENT;
dialog = gtk_dialog_new_with_buttons ("Message",
parent,
flags,
_("_OK"),
GTK_RESPONSE_NONE,
NULL);
content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
label = gtk_label_new (message);
g_signal_connect_swapped (dialog,
"response",
G_CALLBACK (gtk_widget_destroy),
dialog);
gtk_container_add (GTK_CONTAINER (content_area), label);
gtk_widget_show_all (dialog);
}
However, above is giving error:
$ valac --pkg gtk+-3.0 mysrc.vala
rnskeletalGUI_dialog_soques.vala:50.21-50.21: error: syntax error, expected identifier
GtkWidget *dialog, *label, *content_area;
^
Compilation failed: 1 error(s), 0 warning(s)
Where is the problem and how can it be solved?

Two problems.
First, it's Gtk.Widget not GtkWidget. Or, if you have a using Gtk; in that code too, you can just use Widget.
Second, and the one valac is erroring about, is that unlike C, in Vala the pointer goes with the type, not the variable. So if you want three pointers to Gtk.Widget you should write GtkWidget* dialog, label, content_area; not Gtk.Widget *dialog, *label, *content_area;
That said, you shouldn't be using pointers here. Pointers in Vala basically opt you out of the memory management system, and you should almost never use them... think of it a bit like the "unsafe" keyword in some languages.

Related

gtk_container_add: assertion 'GTK_IS_WIDGET (widget)' failed

public class Epoch.MainWindow : Gtk.ApplicationWindow {
private Gtk.Grid grid;
private Epoch.LabelsGrid labels;
private Epoch.PreferencesView preferences_view;
private Epoch.MainView main_view;
public MainWindow (Application app) {
Object (
application: app,
icon_name: "com.github.Suzie97.epoch",
resizable: false,
title: _("Epoch"),
width_request: 500
);
}
construct {
get_style_context ().add_class ("rounded");
set_keep_below (true);
stick ();
var preferences_button = new Gtk.Button.from_icon_name ("open-menu-symbolic", Gtk.IconSize.SMALL_TOOLBAR);
preferences_button.valign = Gtk.Align.CENTER;
var preferences_stack = new Gtk.Stack ();
preferences_stack.add (preferences_view);
preferences_stack.add (main_view);
preferences_stack.transition_type = Gtk.StackTransitionType.SLIDE_LEFT;
var headerbar = new Gtk.HeaderBar ();
headerbar.show_close_button = true;
var headerbar_style_context = headerbar.get_style_context ();
headerbar_style_context.add_class ("default-decoration");
headerbar_style_context.add_class (Gtk.STYLE_CLASS_FLAT);
headerbar.pack_end (preferences_button);
set_titlebar (headerbar);
var main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
main_box.pack_start (preferences_stack, true, true);
add (main_box);
show_all ();
preferences_view = new Epoch.PreferencesView ();
preferences_button.activate.connect (() => {
preferences_stack.visible_child = preferences_view;
});
// public override bool configure_event (Gdk.EventConfigure event) {
// int root_x, root_y;
// get_position (out root_x, out root_y);
// Epoch.settings.set_int ("window-x", root_x);
// Epoch.settings.set_int ("window-y", root_y);
// return base.configure_event (event);
}
}
The following error is displayed when I compile,
I want the app to switch to the preferences view when the button on the right side of the header bar is clicked,
Only, the headerbar is displayed when as you can see.
Why is this happening? How do I solve it?
preferences_stack.add (preferences_view);
preferences_stack.add (main_view);
You haven't initialized preferences_view yet, and you never initialize main_view. That's where your second and third errors are coming from: gtk_container_add() is complaining that the widget you are trying to add is not actually a widget, but rather null, because you haven't initialized that variable yet.

C++/CLI passing additional arguments to event handling method

I'm trying to write a C++/CLI forms application that creates a lot of buttons at runtime: I have a vector of strings and for each string a button is being created:
std::vector<std::string> strings;
/*
string being initialized with values from file
*/
for ( std::vector<std::string>::iterator it = heroes.begin(); it != heroes.end(); ++it ) {
Button ^ button = gcnew Button;
/*
button being customized depending on the string
*/
buttonPannel->Controls->Add(button);
}
Now what I want to do is add an event handler for each button in a way that the string used to customize the button would be passed to handling method.
In c# I would have written something like
button->Click += new EventHandler((sender, args) => button_Click(s, e, *it));
How do I achieve this in C++/CLI?
You could do the exact equivalent of your C# code, but I'd rather make use of an existing property on the Button class to hold the extra data you need.
In this case, the Tag property seems appropriate: its purpose is to hold any extra data you need that is closely associated with the control, so this seems on-point for your string that drives the program logic. (You may need to make it a managed String^ object, rather than a std::string, but that's an easy conversion.)
void Form1::CreateButtons()
{
for (std::vector<std::string>::iterator it = heroes.begin(); it != heroes.end(); ++it)
{
Button ^ button = gcnew Button;
button->Tag = marshal_as<String^>(*it);
button->Click += gcnew EventHandler(this, &Form1::button_Click);
buttonPanel->Controls->Add(button);
}
}
void Form1::button_Click(Object^ sender, EventArgs^ e)
{
Control^ senderControl = dynamic_cast<Control^>(sender);
String^ heroName = nullptr;
if(senderControl != nullptr)
heroName = dynamic_cast<String^>(senderControl->Tag);
if(heroName == nullptr)
{
// Something went wrong. Bail out.
return;
}
// ...
}
If you really do want to do the equivalent of your C# code: Your C# lambda is doing variable capture on the it variable. We can do variable capture in C++/CLI, it's just a lot more manual.
(Note: Your C# example is capturing the iterator, not the string, not sure if that's what was intended. I wrote this to capture the string object instead.)
ref class EventHandlerStringCapture
{
public:
EventHandlerStringCapture(std::string str,
Action<Object^, EventArgs^, std::string>^ handler)
{
this->str = str;
this->handler = handler;
}
void eventHandler(Object^ sender, EventArgs^ e)
{
this->handler(sender, e, this->str);
}
private:
std::string str;
Func<Object^, EventArgs^, std::string>^ handler;
}
void Form1::CreateButtons()
{
for (std::vector<std::string>::iterator it = heroes.begin(); it != heroes.end(); ++it)
{
Button ^ button = gcnew Button;
// The variable to capture.
std::string str = *it;
// The actual event handler: a method in the current class.
Action<Object^, EventArgs^, std::string>^ actualHandler =
gcnew Action<Object^, EventArgs^, std::string>(this, &Form1::button_Click);
// Pass both the variable to capture and the
// actual event handler to a helper object.
EventHandlerStringCapture^ ehsc =
gcnew EventHandlerStringCapture(str, actualHandler);
// Grab the two-parameter event handler from the helper object,
// and make that the click handler.
button->Click +=
gcnew EventHandler(ehsc, &EventHandlerStringCapture::eventHandler);
buttonPanel->Controls->Add(button);
}
}
void Form1::button_Click(Object^ sender, EventArgs^ e, std::string heroName)
{
// ...
}
(Note: I'm not at a compiler, so there may be syntax errors.)
Obviously, using an existing property on the button object is simpler, but that's the C++/CLI equivalent to what the C# compiler does behind the scenes.

Accelerators stop responding when menubar gets hidden

I have a glade-based UI for gtk3, and I set accelerators fields for several menuitems.
I'm not sure what GtkBuilder does exactly behind the scenes when it's loading the glade file (use a global GtkAccelGroup?), but the end result is, when I hide the menubar, the accelerator shortcuts stop working.
I'm wondering whether there is a way of getting the accelerators working even when the menu is not visible, while still sticking to glade as much as possible.
Maybe you can try to stick the accelerators not to the menu, but one level higher in your application, for example the window? In my own application I do it like this.
accel_group = gtk_accel_group_new ();
gtk_window_add_accel_group (GTK_WINDOW (pad), accel_group);
pad->priv->menu = menu_get_popup_no_highlight (pad, accel_group);
pad->priv->highlight_menu = menu_get_popup_highlight (pad, accel_group);
gtk_accel_group_connect (accel_group, GDK_KEY_Q, GDK_CONTROL_MASK, 0, g_cclosure_new_swap (G_CALLBACK (xpad_app_quit), pad, NULL));
The two menu assignments have their own accelerators which are working even when not visible.
Does this help you?
Here is my solution for xournalpp which walks the menubar and rebinds every accelerator to the main window:
Header
class MainWindow: public GladeGui {
public:
void rebindMenubarAccelerators();
private:
static void rebindAcceleratorsMenuItem(GtkWidget* widget, gpointer user_data);
static void rebindAcceleratorsSubMenu(GtkWidget* widget, gpointer user_data);
static gboolean isKeyForClosure(GtkAccelKey* key, GClosure* closure, gpointer data);
static gboolean invokeMenu(GtkWidget* widget);
GtkAccelGroup* globalAccelGroup;
}
Implementation
gboolean MainWindow::isKeyForClosure(GtkAccelKey* key, GClosure* closure, gpointer data) { return closure == data; }
gboolean MainWindow::invokeMenu(GtkWidget* widget) {
// g_warning("invoke_menu %s", gtk_widget_get_name(widget));
gtk_widget_activate(widget);
return TRUE;
}
void MainWindow::rebindAcceleratorsMenuItem(GtkWidget* widget, gpointer user_data) {
if (GTK_IS_MENU_ITEM(widget)) {
GtkAccelGroup* newAccelGroup = reinterpret_cast<GtkAccelGroup*>(user_data);
GList* menuAccelClosures = gtk_widget_list_accel_closures(widget);
for (GList* l = menuAccelClosures; l != NULL; l = l->next) {
GClosure* closure = reinterpret_cast<GClosure*>(l->data);
GtkAccelGroup* accelGroup = gtk_accel_group_from_accel_closure(closure);
GtkAccelKey* key = gtk_accel_group_find(accelGroup, isKeyForClosure, closure);
// g_warning("Rebind %s : %s", gtk_accelerator_get_label(key->accel_key, key->accel_mods),
// gtk_widget_get_name(widget));
gtk_accel_group_connect(newAccelGroup, key->accel_key, key->accel_mods, GtkAccelFlags(0),
g_cclosure_new_swap(G_CALLBACK(MainWindow::invokeMenu), widget, NULL));
}
MainWindow::rebindAcceleratorsSubMenu(widget, newAccelGroup);
}
}
void MainWindow::rebindAcceleratorsSubMenu(GtkWidget* widget, gpointer user_data) {
if (GTK_IS_MENU_ITEM(widget)) {
GtkMenuItem* menuItem = reinterpret_cast<GtkMenuItem*>(widget);
GtkWidget* subMenu = gtk_menu_item_get_submenu(menuItem);
if (GTK_IS_CONTAINER(subMenu)) {
gtk_container_foreach(reinterpret_cast<GtkContainer*>(subMenu), rebindAcceleratorsMenuItem, user_data);
}
}
}
// When the Menubar is hidden, accelerators no longer work so rebind them to the MainWindow
// It should be called after all plugins have been initialised so that their injected menu items are captured
void MainWindow::rebindMenubarAccelerators() {
this->globalAccelGroup = gtk_accel_group_new();
gtk_window_add_accel_group(GTK_WINDOW(this->getWindow()), this->globalAccelGroup);
GtkMenuBar* menuBar = (GtkMenuBar*)this->get("mainMenubar");
gtk_container_foreach(reinterpret_cast<GtkContainer*>(menuBar), rebindAcceleratorsSubMenu, this->globalAccelGroup);
}

how to use the set_cell_data_func function in vala to change the layout of my cell depending on its content?

I'm trying to use the set_cell_data_func on a Gtk.TreeViewColumn. My code compiles but gives a segmentation error when running. I have also tried the insert_column_with_data_func instead but the result is the same.
Here is my code, i hope you'll be able to help me :)
public class Application : Gtk.Window {
public Application () {
this.destroy.connect (Gtk.main_quit);
this.set_default_size (600, 400);
Gtk.ListStore store = new Gtk.ListStore (2, typeof(int),
typeof(string));
Gtk.TreeIter iter;
store.append (out iter);
store.set (iter, 0, 0, 1, "A");
store.append (out iter);
store.set (iter, 0, 1, 1, "B");
store.append (out iter);
store.set (iter, 0, 0, 1, "C");
store.append (out iter);
store.set (iter, 0, 0, 1, "D");
Gtk.TreeView view = new Gtk.TreeView.with_model (store);
this.add(view);
Gtk.CellRendererText cell = new Gtk.CellRendererText ();
Gtk.TreeViewColumn col = new Gtk.TreeViewColumn.with_attributes (
"Value", cell, "text", 1);
col.set_cell_data_func (cell, (Gtk.CellLayoutDataFunc)render_value);
view.append_column (col);
}
public void render_value (Gtk.TreeViewColumn column, Gtk.CellRendererText
cell, Gtk.TreeModel model, Gtk.TreeIter iter) {
var val = Value (typeof(int));
model.get_value (iter, 0, out val);
if ((int)val==1) (cell as Gtk.CellRendererText).background = "#b8cb04";
val.unset ();
}
public static int main (string[] args) {
Gtk.init (ref args);
Application app = new Application ();
app.show_all ();
Gtk.main ();
return 0;
}
}
After debugging the translated c sources i found the bug.
vala translates
public void render_value (Gtk.TreeViewColumn column,
Gtk.CellRendererText cell,
Gtk.TreeModel model,
Gtk.TreeIter iter)
from your code to the following c equivalent
void application_render_value (Application* self,
GtkTreeViewColumn* column,
GtkCellRendererText* cell,
GtkTreeModel* model,
GtkTreeIter* iter)
I compared this with the reference docs.
There the signature of the data function is defined as follows
void (*GtkCellLayoutDataFunc) (GtkCellLayout *cell_layout,
GtkCellRenderer *cell,
GtkTreeModel *tree_model,
GtkTreeIter *iter,
gpointer data);
With regard to the method you have implemented that means that the arguments are shifted by one to the right. So the following applies to your data func/method
column is in fact the cell renderer
cell is the tree model
model is the iterator and
iter is the data that is passed to the function/method
If you change the declaration of your data func/method to
public void render_value (Gtk.CellRendererText cell,
Gtk.TreeModel model, Gtk.TreeIter iter, Object data)
everything should work fine.
Maybe the reason of this is that CellLayoutDataFunc is defined as public delegate void. But since i don't know anything about vala it's just a guess. If it's not the case you may post this on a related vala mailing list.

gtk: how to hide a window when the application loses focus

I want to duplicate the behaviour of tool windows in OpenOfice. When the application loses focus, the tool windows (if they are not docked) are hidden.
So, I have a main window, and another utility window (win_dock). I want to hide win_dock when all the windows of the application loses focus and show it again if a window gain focus.
What I did is that I connected to the focus-in-event and focus-out-event of all windows of the application, and I maintain a counter of how many windows have focus. When this counter drops to zero, I want to hide win_dock, and if this counter is positive again, I want to show win_dock
The problem is with this solution I can never focus win_dock. Because when I click on it, the main window drops the focus, so it hides win_dock that still hadn't gained the focus. Nevertheless the focus-in-event is still sent to win_dock and the windows reappears. But in the meantime it has lost the focus.
Do you have a better solution?
Here is the Vala source code:
public class Main
{
private Gtk.Builder builder;
private Gtk.Window win_messages;
private Gtk.Window win_dock;
private int focus_count = 0;
public Main() {
builder = new Gtk.Builder();
builder.add_from_file("ui2.glade");
win_messages = builder.get_object("win_messages") as Gtk.Window;
win_dock = builder.get_object("win_dock") as Gtk.Window;
handle_focus(win_messages);
handle_focus(win_dock);
}
public void start(){
win_messages.show_all();
//win_dock.show_all();
Gtk.main();
}
private void handle_focus(Gtk.Window w) {
w.focus_in_event.connect ((w, e) => {
stdout.printf("Focus In (%s)\n", w.name);
focus_count++;
manage_focus(w == win_dock);
});
w.focus_out_event.connect((w, e) => {
stdout.printf("Focus Out (%s)\n", w.name);
focus_count--;
manage_focus(w == win_dock);
});
}
private void manage_focus(bool is_dock){
if(focus_count > 0) {
win_dock.show_all();
stdout.printf("Show (focus: %d)\n", focus_count);
} else if(is_dock) {
win_dock.hide_all();
stdout.printf("Hide (focus: %d, has: %d) dock\n", focus_count, win_dock.is_active ? 1 : 0);
} else if(!is_dock) {
if(win_dock.is_active) {
win_dock.hide_all();
stdout.printf("Hide (focus: %d, has: %d) !dock\n", focus_count, win_dock.is_active ? 1 : 0);
} else {
stdout.printf("Nop (focus: %d, has: %d) !dock\n", focus_count, win_dock.is_active ? 1 : 0);
}
}
}
public static int main (string[] args)
{
Gtk.init (ref args);
Main m = new Main();
m.start();
return 0;
}
}
Thanks.
Is there a good reason to make the dialog disappear? Wouldn't it be enough to make win_dock transient (win_dock.set_transient_for) for the main window?
Otherwise you could try using GLib.Idle.add to call manage_focuswhich will cause
manage_focus to run after all your focus event callbacks have run. It will then have the correct number of focused windows.