Difference between GtkWindow and GdkWindow? - gtk

At the beginning of my Gtk-Gdk-Cairo-Pango app, I create the window:
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
First, there is GtkWindow, but gtk_create_window returns GtkWidget, not GtkWindow, why?
Then, some functions, like gdk_window_process_updates(..) require GdkWindow*.
gtk_window_set_geometry_hints() on the other hand requires GtkWindow*.
In documentation there is also GdkWindow* gdk_window_new() that returns GdkWindow.
Sure there is documentation saying:
A GdkWindow is a rectangular region on the screen. It's a low-level
object, used to implement high-level objects such as GtkWidget and
GtkWindow on the GTK+ level. A GtkWindow is a toplevel window, the
thing a user might think of as a "window" with a titlebar and so on; a
GtkWindow may contain many GdkWindow.
But it still does not tell me, when and why I should create Gtk or Gdk windows?
What is the pattern here to follow?
Now you ask, what particular problem I am trying to solve? Sure, I try to draw text using cairo+pango on top of gtk+gdk, right after mouse moves. The problem is that although the actual drawing seems to be fast performing, I cannot get it happen exactly as mouse moves. In my motion_notify_event I just call gtk_widget_queue_draw(GtkWidget) but there is obvious lag behind the actual mouse moving on screen, even if I draw single character it is not aligned with the mouse pointer during the move phase and only catches it after the mouse is stopped.
What I tried is to speed up the update by calling gdk_window_process_updates(GDK_WINDOW(window), false);, the compiler eats it, but I got runtime assertion: Gdk-CRITICAL **: gdk_window_process_updates: assertion 'GDK_IS_WINDOW (window)' failed. I cannot find any information on this macro and how/when to use it.
#include <cairo.h>
#include <gtk/gtk.h>
#define TXT "1234567890"
int X = 0, Y = 0;
static void do_drawing(cairo_t *);
GtkWidget *window;
PangoLayout *layout = 0;
static gboolean on_draw_event(GtkWidget *widget, cairo_t *cr,
gpointer user_data) {
do_drawing(cr);
return FALSE;
}
static void do_drawing(cairo_t *cr) {
if (layout == 0) {
layout = pango_cairo_create_layout (cr);
pango_layout_set_text (layout, TXT, -1);
}
for (int y = 0; y < 2; y++) {
cairo_set_source_rgb (cr, 1, 0, 1);
cairo_move_to (cr, 0+X, 0 + y * 20 + Y);
pango_cairo_show_layout (cr, layout);
}
gtk_widget_queue_draw(window);
}
static gint onmouse(GtkWidget *widget, GdkEventMotion *event) {
X = event->x; Y = event->y;
gtk_widget_queue_draw(widget);
gdk_window_process_updates(GDK_WINDOW(widget), false);
}
int main(int argc, char *argv[]) {
GtkWidget *darea;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
darea = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(window), darea);
gtk_widget_set_events (window, GDK_EXPOSURE_MASK
| GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK);
g_signal_connect(G_OBJECT(darea), "draw", G_CALLBACK(on_draw_event), NULL);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(window, "motion_notify_event", G_CALLBACK(onmouse), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 5000, 5000);
gtk_window_set_title(GTK_WINDOW(window), "Lines");
gtk_widget_show_all(window);
gtk_main();
return 0;
}

Window managers (X11, Wayland, Windows's user32.dll, and the one in Mac OS X whose name I don't remember) do not (necessarily) provide much functionality on their own. What they give you is:
the ability to create on-screen regions, called windows, which you can draw on, move around, resize, reshape, minimize, hide behind other windows, and control in other basic ways — but the most important of these for our discussion is "draw on"
mouse event handling for windows: notifications when the mouse moves into or out of a window, around inside a window, or when the mouse buttons are clicked when the mouse cursor is over a window
the concept of a window which receives keyboard input (the focused window) and keyboard events for this window: when the user types into a window
miscellaneous other functions (drawing mouse cursors, for instance)
When combined with a facility to do vector graphics and text rendering into a window (which is often provided by other libraries, such as cairo and pango), the GUI toolkit comes into play. This is what takes the window manager window and divides it into all the little controls that you're familiar with: buttons, text fields, lists, tabs, web page renderers, etc.
GTK+ is the GUI toolkit in this case. It provides the plethora of controls that you use in your programs.
When you use a GUI toolkit, you don't typically interact with the window manager directly. So instead, the GUI toolkit provides its own window. When you create a GUI toolkit window, the GUI toolkit creates the underlying window manager window, then takes control of all the drawing and events so that it can handle the work of giving you all those neat controls in that window.
For GTK+, this is GtkWindow.
The designers of GTK+ did not want to have all the window manager interaction code for each individual platform that GTK+ supports in GTK+ itself. Instead, they created a separate library (included with the GTK+ source code), called GDK. GDK provides a consistent portable API around the low-level platform-specific window manager functions.
So GdkWindow is the type that wraps around a window manager window and provides the portable interface that GTK+ uses. When you create a GdkWindow, you're creating one of these low-level window manager windows, not the richer GtkWindow that you place controls on.
X11 has historically been very resource-constraining. GTK+ doesn't create a window manager window for every control; it only creates these for GtkWindow, GtkPopover, and other similar controls that act as what we as users think of as windows.
Armed with all this knowledge, you can now figure the answer to your question: you almost always want to use GtkWindow, and almost never want to use GdkWindow. GdkWindow is only really useful for the implementation of certain GTK+ controls.
And GdkWindow and GtkWindow are NOT interchangeable.
(This is a still-reasonably-accurate oversimplification of what's going on. It does not hold true for all environments. People who write native Windows programs, for instance, generally do create window manager windows for each control, and the window manager provides some basic controls, such as buttons. I may have also gotten some details in the above explanation wrong.)
The separation between GDK and GTK+ also has several other advantages. Adding Wayland support, for instance, did not (as far as I know; I could very well be wrong about this) require many changes to GTK+ itself, and there is a GDK layer called broadway which lets normal GTK+ programs render in a web browser.
Updates, since I seem to be linking this a lot:
There was a time when most GtkWidgets actually had their own GdkWindows; here's what happened. Now, the situation I described is the case.

There's so many questions in there that I'm not going to try answering all.
About latency of drawing: most likely option is that there's a bug or unoptimized code in your implementation: the draw cycle is quite unique in application code in that it really, really needs to be fast...
Things to note:
you call gtk_widget_queue_draw(window) from your draw-event handler: that seems unnecessary
you always redraw on a mouse event without checking if it's really necessary (possibly you do want to queue draw on all motion events: please make sure of that though)
you always redraw the whole widget: this can be very expensive and you shouldn't do it if you only need to change a small area at a time and are doing frequent redraws like you are. See gtk_widget_queue_draw_region ().
drawing text can be expensive: I'm not familiar with the functions you use but you may want to start with just drawing a box or something to see where the problem is

Related

Don't pass through UIToolkit buttons

I've been playing around with UIToolkit, and it's awesome. Though my clicks pass through the buttons, so when clicking the UI my raycast also starts. Is there a way to check if a click/touch is on the GUI?
Edit: I'm using Prime31's UIToolkit
Solved:
foreach (var touchable in _gui.touchableSprites) {
if ( !touchable.hidden && touchable.hitTest(new Vector2(clickPos.x, Screen.height - clickPos.y)) ) return;
}
Thanks.
Assuming you're using prime31's UIToolkit, take a look at the UIToolkit.cs(lines 81-95)
) script The author works an example showing how to handle highlighting his TouchableSprites. The same functionality can be adapted to doing a MouseOver event. I'm not sure if the author has added this functionality since 2011, I've not used UIToolkit in awhile.
Alternatively you can modify all of your ray tracing to return if the first layer hit is "UILayer" (UIToolkit's layer).
A third method, one that I use, is to create a global GUI element manager that stores a list of all GUI rects. When I add a new ray tracing function I call my manager class to see if the mouse cursor is contained in any GUI rect (there is a bit more sophistication that merges overlapping rects), if so, the ray trace is skipped.

Mac style joined buttons (segmented control) with Gtk

I've seen it done in the ubuntu software center, and with a few gnome apps.
Where two buttons look like a single rectangle with a line through it, how is this being done?
In GTK 3.0 and later, use the INLINE_TOOLBAR style class or LINKED style class.
#!/usr/bin/env python
from gi.repository import Gtk
button_names = [
Gtk.STOCK_ABOUT,
Gtk.STOCK_ADD,
Gtk.STOCK_REMOVE,
Gtk.STOCK_QUIT]
buttons = [Gtk.ToolButton.new_from_stock(name) for name in button_names]
toolbar = Gtk.Toolbar()
for button in buttons:
toolbar.insert(button, -1)
style_context = toolbar.get_style_context()
style_context.add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR)
window = Gtk.Window()
window.set_size_request(200, 50)
window.add(toolbar)
window.connect('delete-event', Gtk.main_quit)
window.show_all()
Gtk.main()
Fwiw, since I recently had the same question, here's another answer with an up-to-date citation and using C instead of some wrapper. (I use GTKmm, but I feel we should refer to the native C API unless a wrapper is explicitly stated.)
I wanted to achieve the same thing and eventually stumbled upon the answer, at the official page HowDoI: Buttons. This effect is achieved by putting the targeted Buttons in a Container and giving the latter the CSS class linked. (That page says using a Box is normal, but asBox will probably be deprecated soon and Grid is superior anyway... use a Grid.)
So:
GtkWidget *const grid = gtk_grid_new ();
GtkStyleContext *const context = gtk_widget_get_style_context (grid);
gtk_style_context_add_class (context, "linked");
/* Add your Buttons to the Grid */
That link also discusses some other handy embellishments we can make to GtkButtons, including the classes suggested-action and destructive-action.
For similar questions, it's very illustrative to browse the source of (A) an application that does what you want to replicate and (B) the Adwaita theme CSS.

Remove scroll ability from GtkSpinButton

I find that GtkSpinButton can be useful for controlled input numbers on GTK GUI.
But here are some nice features of GTK button which can be unwanted in many cases.
If we have GtkSpinButton inside scrolledwindow then user can accidentally change value, or GtkSpinButton can take scroll behaviour from scrolledwindow.
Question: Is here any possibility to make GtkScrollButton insensible to mouse wheel, at way like is GtkEntry. Or better, could GtkSpinButton be shown without up/down buttons.
If not, how to redirect scroll signal from GtkSpinButton to scrolledwindow?
I try this:
SCROLL_numgreen (GtkObject *object, GdkEvent *event, gpointer user_data)
{
switch (((GdkEventScroll *)event)->direction)
{
case GDK_SCROLL_UP:
return TRUE;
break;
case GDK_SCROLL_DOWN:
return TRUE;
break;
... etc...
but this only "eats" scroll signal from GtkSpinButton and block scrolledwindow at place.
I would most like some general solution without intervention to events of every GtkSpinButton.
Here a few pointers to your queries:
Is here any possibility to make GtkScrollButton insensible to mouse wheel, at way like is GtkEntry?
Mouse wheel scroll, mouse click are events. The events can be masked. This can be done at two levels.
At GtkWidget level: you can use gtk_widget_get_events() which will return the event mask in the form of GdkEventMask. You can modify this as per your need & set it using gtk_widget_set_events()
At GdkWindow level: GtkWidget which has its own drawing/eventing area has a GdkWindow associated with it. You can get the event mask of this window using gdk_window_get_events() and change returned GdkEventMask as per your need & set it to the GdkWindow using gdk_window_set_events(). You can modify event mask through bit-wise operations. If GdkWindow is shared between more than one widget then this mask will effect all the widgets. For masking scroll events you can look into GDK_SCROLL_MASK, GDK_BUTTON_PRESS_MASK & GDK_BUTTON_RELEASE_MASK. You can always check the mask for the event which you are looking for is already set or not. Note: GdkWindow related calls will succeed only after GdkWindow is created for GtkWidget. You can make these calls after gtk_widget_show() of the widget or gtk_widget_show_all of the window which contains these widgets.
Or better, could GtkSpinButton be shown without up/down buttons.
AFAIK GtkSpinButton is implemented to have up/down button indicative of the functionality it provides. If you don't want this, then you can choose another widget say GtkEntry (from which GtkSpinButton is "derived") or GtkLabel. Of course you can create your own widget (from scratch or "derive" from an existing GtkWidget) as per your need & use that same; there is no one stopping you from doing this :)
How to redirect scroll signal from GtkSpinButton to scrolledwindow?
It is possible to do this in the "scroll-event" callback of GtkSpinButton. You can stop the emission of the signal on GtkSpinButton & return FALSE to propagate the event.
...
/* Event callback */
gboolean spinbutton_scroll_handler(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
/* Stop emission on current widget. Default handler also not called */
/* Refer http://developer.gnome.org/gobject/stable/gobject-Signals.html#g-signal-stop-emission-by-name */
g_signal_stop_emission_by_name(widget, "scroll-event");
/* Return FALSE to propagate the event further; thus scroll window will scroll
If TRUE returned other handlers not invoked for this event,
thus no scroll on scroll window */
return FALSE;
}
...
/* Connect scroll-event to the callback */
g_signal_connect(spinbutton, "scroll-event",
G_CALLBACK(spinbutton_scroll_handler),
(gpointer)0);
...
Hope this helps!

gtkmm button not maintaining size and location

I have created two gtkmm button and added to HBox object. I called pack_end, and maintained the size as 21,20. But, the sizes are not maintained. Here is the code i have written and the window that i got while running the program.
Note: MYWindow is subclass of Gtk::Window
void MYWindow::customizeTitleBar()
{
//create a vertical box
Gtk::VBox *vBox = new Gtk::VBox(FALSE,0);
//create a horizontal box
Gtk::HBox *hBox = new Gtk::HBox(TRUE,0);
hBox->set_border_width(5);
//create title bar image
Gtk::Image *titleBarImage = new Gtk::Image("src/WindowTitleBar.png");
titleBarImage->set_alignment(Gtk::ALIGN_LEFT);
// hBox->pack_start(*titleBarImage,Gtk::PACK_EXPAND_WIDGET,0);
//create cloze button for window
mButtonClose = new Gtk::Button;
(*mButtonClose).set_size_request(21,20);
Gtk::Image *mImage = new Gtk::Image("src/Maximize.jpeg");
(*mButtonClose).add(*mImage);
(*mButtonClose).set_image_position(Gtk::POS_TOP);
// connecting close window function when cliked on close button
//(*mButtonClose).signal_clicked().connect( sigc::mem_fun(this, &MYWindow::closeWindow));
hBox->pack_end(*mButtonClose,Gtk::PACK_EXPAND_WIDGET,0);
Gtk::Button * mBtton = new Gtk::Button;
mBtton->set_size_request(21,20);
Gtk::Image *img = new Gtk::Image("src/Maximize.jpeg");
mBtton->add(*img);
mBtton->set_image_position(Gtk::POS_TOP);
hBox->pack_end(*mBtton,Gtk::PACK_EXPAND_WIDGET,0);
vBox->add(*hBox);
//drawing area box
Gtk::HBox *hBoxDrawingArea = new Gtk::HBox;
Gtk::DrawingArea *mDrawingArea = new Gtk::DrawingArea;
hBoxDrawingArea->pack_start(*mDrawingArea,Gtk::PACK_EXPAND_WIDGET,0);
vBox->add(*hBoxDrawingArea);
//status bar hBox
Gtk::HBox *hBoxStatusBar = new Gtk::HBox;
vBox->add(*hBoxStatusBar);
this->add(*vBox);
this->show_all();
}
I am not yet a gtk expert (but I'm learning), here's one thing you can try, which is what I've been doing.
Make a little standalone project using glade. Glade makes it really easy to screw around with all the packing settings so you can immediately see the effects of your changes.
I think in the case of resizing the window, you'll have to save the glade file and run your program (using gtkbuilder to render the glade file) and manually resize the window to see the effect, but once you make the standalone project, you can use it for other gtk testing.
And if you're like me, you'll get swayed by the wonderfulness that is glade and build your whole system that way.
But basically, it sounds like a packing issue, because I've got buttons that don't resize all over the place.
As for not moving, I'm not sure you can do that, but again I'm not an expert. I think you should be able to pin the size of some if not all of the hbox pieces so that the button inside them will not move, but I'm not sure what happens if you don't have any hbox parts that can't be variably sized to take up the slack when you grow the window.
Again, sounds like something fun to try in glade. :-)
I think you pack to FALSE , Maybe this is the problem :
Gtk::HBox *hBox = new Gtk::HBox(TRUE,0)
I use python gtk with something like this:
box1.pack_start(box2,False)

Drawing into an Eclipse editor

I am trying to draw some shapes (boxed ans arrows) into, i.e., "over" the text in an eclipse editor. To get started, I wrote the following code:
IWorkbenchPage activePage = Activator.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage();
final Shell shell2 = activePage.getActiveEditor().getSite().getShell();
shell2.addPaintListener(new PaintListener(){
public void paintControl(PaintEvent e){
Rectangle clientArea = shell2.getClientArea();
e.gc.drawLine(0,0,clientArea.width,clientArea.height);
}
});
The problem with this code is twofold: (1) The line is drawn not across the editor but across the entire workbench, i.e., Eclipse window, and (2) the line is drawn behind (!) all other controls like toolbars and editors. This causes the line to be almost invisible: it only shows at some pixels between other controls.
How can I draw a line across a control like a text editor in Eclipse?
The problem that you have is that you are getting the Shell, not the actual component for the editor. The Shell is the whole window where Eclipse is being shown.
I think the only solution is to create your own Editor implementation, and then in the createPartControl() method you can create a text area and then add the paint listener to it.
You can get started with:
http://www.realsolve.co.uk/site/tech/jface-text.php
And then, looking at the source code of AbstractTextEditor, you can find the "real" SWT component that you want to draw to. You would need to override the method that creates the UI components, copy the original code and add your custom painting.
I'm not sure if it works, but you need to extend the TextEditor:
public class MyEditor extends TextEditor {
protected StyledText createTextWidget(Composite parent, int styles) {
StyledText widget = super.createTextWidget( parent, styles );
widget.addPaintListener( <yourPaintlistener> );
return widget;
}
}
That should at least get you the basic text-drawing control of the editor. Still, it's a PITA to work with these classes, as it is very internal stuff from eclipse, and neither documented nor really extensible.
Good luck with that :)