3
votes

I'm trying to implement an image viewer using GTK+3, and it's supposed to have two modes:

  1. Fit-to-something mode (fit-to-width/height)
  2. Original size mode

Now, in both modes, I need the image are to be scrollable, and in fit-to-X, I need to know the actual size of the "displayed image area" which is actually the directly visible window region. Also, since the window could be resized, I need to scale the image dynamically.

In Qt, it is just a breeze. No 3rd party libraries (gdkpixbuf), no need to create a custom widget, no worries.

I can set the image for a QLabel, and on resize, I can smoothly scale the image (without relying on any 3rd party libraries such as gdkpixbuf) to the new size (it seems it's not possible to get the current size with GTK+ due to some convoluted reasons which clearly don't apply to Qt; in order to get the size of the "image arena", Comix gets the total window dimensions, subtracts dimensions of menubar, scrollbar, toolbar and scrollbars in get_visible_area_size if they are visible!) . With correct parameters, this plays well both with expanding and shrinking, and the parent scroll area can do the scrolling depending on the size of QLabel.

But to my surprise, such an elementary thing, if I'm not missing something, is not possible using out-of-the box widgets with GTK+.

Here's what I have tried so far:

  • Using a Layout directly, moving Image object around manually, and adding & handling horizontal and vertical scrollbars manually (and of course, handle signals and move()ing of the image inside Layout manually). This is what Comix does! This is terrible, so I stopped doing this in the mid-way.

  • Placed an image object inside ScrolledWindow + Viewport: scrolls nice and neat, but the problem is, I don't know the visible size of the "directly visible" part of the ScrolledWindow (nor the encapsulating window or Viewport); get_allocation returns the size of the total image area, that is, the visible part plus the non-visible part (that user can see by scrolling). The allocation for Viewport reports the correct size after "ordinary" resize events, but it reports the previous size after maximize/minimize (I'm listening at both window-state-event and configure-event). A behavior which I think is a long standing GTK+ bug.

Other things I have exhausted:

  • Checking page_size field of adjustment: returns the previous old value after maximization/minimization
  • Checking width and height fields in the configure-event: works ok even after maximization/minimization, but, configure-event is not dispatched for a GtkViewport or GtkScrolledWindow, so I can only use the parent window for that. And unfortunately, the parent window size is larger than viewport due to some other widgets (statusbar, toolbar etc).

The problem would be basically solved if GTK+ provided:

  • A way of obtaining the actual size of the visible area in the ScrolledWindow.
  • And optionally, a way of disabling the scrolling in ScrolledWindow.

My question is, am I missing something obvious here? It's hard to believe such an elementary thing cannot be done directly using out-of-the-box widgets with such an established library. What is the most straightforward way of implementing an image viewer that can do scrolling as well as responsive scaling of the image?

3
Just as a very minor note, GDK and its sub-libraries can hardly be considered "third party" when talking about GTK+. They're no more third party than GTK+ itself.unwind

3 Answers

3
votes

I'm creating an image viewer too! And I can tell you this is actually fairly easy to implement. All you need to do is get the child widget of the GtkScrolledWindow.

The solution I used on my image viewer used a custom widget which implemented the GtkScrollable interface and rendered the images. If you used one, then that custom widget allocation is the dimensions of of the "visible area".

If you are using a GtkViewport instead(through gtk_scrolled_window_add_with_viewport maybe?), then that GtkViewport allocation is the visible area of the GtkScrolledWindow. A GtkViewport child widget is always at its requested size, which might be bigger than the GtkScrolledWindow visible area.

Either way, you use gtk_bin_get_child to get the child widget of a GtkScrolledWindow and then use gtk_widget_get_allocation on that GtkWidget to get its allocation. That allocation is the "visible area" of the scrolled window without the scrollbars.

1
votes

I found that I'm listening for the wrong event: I've listening to "configure-event" of the window which doesn't fire during minimize/maximize. Listening to "size-allocate" solved the problem.

Thanks everyone for their answers!

-1
votes

I think your main point:

A way of obtaining the actual size of the visible area in the ScrolledWindow.

is easily handled by quering the GtkAdjustments that the scrollbars visualize. The "page_size" of the adjustments should correspond to the currently visible area.