Scrolling a very large GtkDrawingArea - c++

I have a GtkDrawingArea that is used to visualize data.
Depending on the database and user input the drawing can grow really large (larger than the maximum allowed GtkDrawingArea size). Therefore I would like to use a drawing area that is just as big as the current window and update it manually upon scrolling.
If I use the ScrolledWindow + Viewport method to add scroll-bars to the drawing area it does obviously not work because the drawing area is not big enough to need scroll-bars.
Is there any way that that I can trick the viewport into thinking that the underlying widget is larger than it actually is?
If not what would be the best way to solve this problem?
Note: I am using Gtk2 and switching to Gtk3 is not a possibility.

You need to subclass GtkDrawingArea and override the set_scroll_adjustments signal. GtkWidget docs
In this signal you will get the adjustments for the scrolled window. I wrote some code a few years back that you can look at to see how to implement it.
MarlinSampleView code
This code was able to pretend that the widget was millions of pixels wide when in reality it wasn't any bigger than the window.

It turned out to be quite simple:
The GtkScrolledWindow has another constructor which can be used to set the GtkAdjustments that the scrolled window should use.
//These adjustments will be attached to the scrollbars.
prvt->hAdjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 0, 0, 0));
prvt->vAdjustment = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, 0, 0, 0, 0));
GtkWidget* scrolledTree = gtk_scrolled_window_new(prvt->hAdjustment, prvt->vAdjustment);
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolledTree), drawing_area);
Now, whenever the size of the drawing changes you just need to modify the GTKAdjustments to simulate the change. There is no need to actually resize the drawing area.
gtk_adjustment_set_lower(prvt->hAdjustment, 0);
gtk_adjustment_set_step_increment(prvt->hAdjustment, 1);
gtk_adjustment_set_page_increment(prvt->hAdjustment, 10);
gtk_adjustment_set_upper(prvt->hAdjustment, picture_width);
gtk_adjustment_set_page_size(prvt->hAdjustment, scrollArea_width);
gtk_adjustment_changed(prvt->hAdjustment);
Notice that I call gtk_adjustment_changed in the end. This is important, otherwise the ScrolledWindow will not update the scrollbars.
Finaly the value_changed callback of the GtkAdjustmens can be used to catch the scroll events and adjust the drawing.
Edit: This does not work properly because the GtkScrolledWindow receives the scroll event
as well and moves the image :(

Related

X11 window does not get refreshed until it gets an event

In my application (cairo and X11), the user can issue a command whereby the drawing is enlarged. To be able to grab the entire drawing as a pattern, I enlarge the drawing surface to match the current scale (the drawing is just a graph, so this can be afforded as far as memory is concerned). Beginning with a certain scale though, the X11 window refuses to refresh until it gets an event (e.g. loss of focus, which is not even handled in my application).
I tried refreshing the window using both XFlush() and XSync().
Does this look like a bug in the windowing system? If not, what should I do? Everything works perfectly with smaller scales.
EDIT 1: After much work with gdb, I found that the problem is not with the window not refreshing. Rather, at a certain point a call to XNextEvent() causes the window to become all black.
EDIT2: It looks like calls to XNextEvent() actually cause the window to be refreshed! And here is the code that caused the problem:
struct PatternLock {
PatternLock(Graphics &g)
: g_(g) {
p_ = cairo_get_source(g_.cr);
cairo_pattern_reference(p_);
}
~PatternLock() {
// The commented lines caused the problem. How come?
// cairo_set_source_rgb(g_.cr, 0, 0, 0);
// cairo_paint(g_.cr);
cairo_set_source(g_.cr, p_);
cairo_paint(g_.cr);
cairo_pattern_destroy(p_);
}
private:
Graphics &g_;
cairo_pattern_t *p_;
};
Suppose the we have this code for moving the drawing:
{
PatternLock lock{g};
... // Change of transformation matrix
}
It somehow happen that the effect of the commented lines in the destructor of PatternLock becomes visible (hence the black screen), but the effect of the following lines does not. I realize that the commented code is actually unneeded. But still, how does this happen?
If my memory serves me correct, there's a limit to Drawables (e.g. Windows and Pixmaps) of 4096x4096 pixels. You should check the return values of your calls to XCreatePixmap() etc.
Either way, just enlarging the pixmap to draw your drawing is Bad Design (tm), and will inevitably lead to a very slow program. Learn how to deal with zoom and pan (tip: work from the center of your viewport, not the corners). Assuming your drawing is vector-based (i.e. lines and curves) you can optimize painting a lot at high zoom factors.
If you must grab a complete graph at a resolutions larger than 4096 pixels you must implement tiling, which isn't that hard if you have zoom and pan already.

How to change looks of a disabled button and edit control?

Enabled
Disabled
When I disable a button ( Created with BS_BITMAP style flag ) it changes its look (Please see the above images), same thing happens with edit controls.
How do I make the controls not change when disabled ?
I can do that by subclassing the control, but is there an easier way ?
I don't want to subclass the controls just for that, if possible.
You do not need to subclass the control in order to do this, although I'd say it would be much cleaner. The alternative to set the BS_OWNERDRAW style and handle the WM_DRAWITEM message. That means you're taking over all drawing, but that's okay since you don't want it to look like a normal button anyway.
I could not agree more with Jonathan Potter's observation that it is extremely bad UI design to fail to indicate to the user which buttons are enabled and which ones are not. There are multiple ways to do this, but not doing it is not a viable option. Fortunately, it is easy to do with WM_DRAWITEM, since it tells you the button's current state.
So make the WM_DRAWITEM message handler look like this (in the parent window's window procedure):
case WM_DRAWITEM:
{
const DRAWITEMSTRUCT* pDIS = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
// See if this is the button we want to paint.
// You can either check the control ID, like I've done here,
// or check against the window handle (pDIS->hwndItem).
if (pDIS->CtlID == 1)
{
// Load the bitmap.
const HBITMAP hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
// Draw the bitmap to the button.
bool isEnabled = (pDIS->itemState & ODS_DISABLED) == 0;
DrawState(pDIS->hDC,
nullptr,
nullptr,
reinterpret_cast<LPARAM>(hBmp),
0, 0, 0, 0, 0,
DST_BITMAP | (isEnabled ? DSS_NORMAL : DSS_DISABLED));
// Delete the bitmap.
DeleteObject(hBmp);
// Draw the focus rectangle, if applicable.
if ((pDIS->itemState & ODS_FOCUS) && ((pDIS->itemState & ODS_NOFOCUSRECT) == 0))
{
DrawFocusRect(pDIS->hDC, &pDIS->rcItem);
}
// Indicate that we handled this message.
return TRUE;
}
break;
}
Naturally, you could optimize this code further by loading the bitmap a single time and caching it in a global object, rather than loading and destroying it each time the button needs painting.
Note that I've used the DrawState function, which can draw bitmaps either in a "normal" (DSS_NORMAL) or "disabled" (DSS_DISABLED) state. That simplifies the code considerably, and allows us to easily handle the disabled state, but unfortunately the result looks a little bit ugly. That's because the DrawState function converts the bitmap to monochrome before applying any effects other than normal.
You probably don't like that effect, so you'll need to do something else. For example, use two separate images, one for the enabled state and the other for the disabled state, and draw the appropriate one. Or convert your normal color image into grayscale, then draw that for the disabled state.
And if the custom-drawing code runs too slowly, you can optimize it even further by checking the value of pDIS->itemAction and only re-drawing the necessary portions.
Then, once you think you've got everything all polished and efficient, the inevitable bug reports will start to roll in. For example, keyboard accelerators are not supported. Then, once you add support for these, you'll need to indicate that in the UI. That will be difficult with a bitmap that already contains the text; the only way to draw a letter underlined is to draw the text yourself. This all proves that owner-draw is way too much work. Just let Windows draw the controls the normal way, don't break everything for your users just because some designer thinks it "looks cool".

How to efficiently render double buffered window without any tearing effect?

I want to create my own tiny windowless GUI system, for that I am using GDI+. I cannot post code here because it got huge(c++) but bellow is the main steps I am following...
Create a bitmap of size equal to the application window.
For all mouse and keyboard events update the custom control states (eg. if mouse is currently held over a particular control e.t.c.)
For WM_PAINT event paint the background to offscreen bitmap and then paint all the updated controls on top of it and finally copy entire offscreen image to the front buffer via Graphics::DrawImage(..) call.
For WM_SIZE/WM_SIZING delete the previous offscreen bitmap and create another one with new window size.
Also there are some checks to prevent repeated drawing of controls i.e. controls are drawn only when it needs repainting in other words when the state of a control is changed only then it is painted e.t.c.
The system is working fine but only with one exception...when window is being resizing something sort of tearing effect appears. Now what I mean by tearing effect I shall try to explain ...
On the sizing edge/border there is a flickering gap as I drag the border.It is as if my DrawImage() function returns immediately and while one swap operation is half done another image drawing starts up.
Now you may think that it is common artifact that happens in many other application for the fact that resizing backbuffer is not always as fast as resizing window are but in other applications I noticed in other applications that although there is a leg between window size and client area size as window grows in size nothing flickers near the edge (its usually just white background that shows up as thin uniform strips along the border).
Also the dynamic controls which move with window resize acts jerky during sizing.
At first it seemed to me that using a constant fullscreen size offscreen surface could minimize the artifact but when I tried it results are not that satisfactory. I also tried to call Sleep() during sizing so that the flipping is done completely before another flip starts but strangely even that won't worked for me!
I have heard that GDI on vista is not hardware accelerated, could that might be the problem?
Also I wonder how frameworks such as Qt renders windowless GUI so smoothly, even if you size a complex Qt GUI window very fast negligibly little artifact appears. As far as I know Qt can use opengl for GUI rendering but that is second option.
If I use directx then real time resizing is even harder, opengl on the other hand seems to be nice for resizing without any problem but I will loose all the 2d drawing capability of GDI+.
If any of you have done anything like this before please guide me. Also if you have any pointer that I should consider for custom user interface design then provide me the links.
Thanks!
I always wished to design interfaces like windows media player 11 but can someone tell me that there is a straight forward solution for a c++ programmer (I want to know how rather than use some existing framework etc.)? Subclassing, owner drawing, custom drawing nothing seems to give you such level of control, I dont know a way to draw semitransparent control with common controls, so I think this question deserves some special attention . Thanks again.
Could it be a WM_ERASEBKGND message that's causing it?
see this question: GDI+ double buffering in C++
Also, if you need fast response from your GUI I would advise against GDI+.

Handling maximized windows using SDL

We recently ported Bitfighter from GLUT to SDL. There were numerous benefits to doing this, but a few drawbacks as well, especially in the area of window management.
Bitfighter runs in a fixed-aspect-ratio window (800x600 pixels). Users can make their window any size they want, but we capture the resize event and make adjustments to the requested size to ensure the window keeps the correct proportions (using SDL_SetVideoMode).
(The following problem applies to Windows, but has not yet been tested on other platforms. What I describe below refers specifically to Windows, though I am looking for a platform-independent solution.)
Ordinarily, this works great, except when users maximze their window by double clicking on the title bar or using the maximize button. In that case, the window resize event is called with the a window size approximating the screen size (minus some pixels for window ornamentation). Unfortunately, when the window is maximized, SDL_SetVideoMode has no effect (unlike GLUT which was able to resize a maximized window). Furthermore, subsequent calls to SDL_GetVideoInfo report the size we requested, not the actual current size of the window, so it is hard to tell if the attempted resizing worked.
I am looking for a platform independent way to do any of the following (in descending order of preference):
Resize a window after it's been maximized
Detect when a window has been maximized so that, knowing I can't resize it, I can at least adjust the video to be centered
Prevent a window from being maximized (block double clicks on window title bar, use of the maximize button, and dragging the window to the top of the screen)
Bitfighter is written in C++, and we're using the latest official release of SDL.
Migrate to SDL 2.0 (which it seems you already have)
SDL 2.0 provides a better API to window management (it actually provides one). While there are still many bugs in Windows management in SDL 2.0 (especially on the Linux side), it has vastly improved since the 1.2 days.
I assume, that you use OpenGL with SDL, because you used GLUT before. I don't know any solutions for that problem, exept point 2. If you want the Video to have a specific size, just leave the SDL-Window like it is, and call
glViewport(0, 0, width, height);
with the right size with the right proportions.
With that solutions you will still have a black border in your window, but It only shows as much, as you want. (with the first 2 arguments you can also set the position of the Viewport in the window ;) )

Flickering child windows with alpha channels

When drawing child controls containing bitmaps with per-pixel alpha channels, we are getting quite a lot of flickering whenever they need to be redrawn. The actual blending is working correctly. I've found a lot of info about reducing flicker (such as this question or this site), but I can't seem to find anything that applies specifically to this situation.
For example, I've got a button with a few different bitmaps that are alpha blended and blitted to the window, depending on the state of the button. When their state changes and I need to draw a different bitmap, I need to redraw the background first, or else it blends with pixels left over from the previous state's bitmap. This is where I am getting some flickering, where I get a bit of the background tearing in occasionally.
The problem is made more complicated by having the top-level parent windows drawing a bitmap background, rather than a solid color, along with the possibility of having child controls overlapping; just multiplying the underlying color into the child's bitmap is out of the question, as is using WS_CLIPCHILDREN.
Since the windows have a bitmap background, I'm returning true on WM_ERASEBKGND, to avoid drawing a color that will just be overwritten.
Of course, double buffering would seem to solve all of this, but I have not been able to get it to work right. I've set WS_COMPOSITED for top-level windows, and WS_TRANSPARENT for child windows. When it comes time to redraw a child window with a new bitmap, I am having a few issues (most likely from me not understanding how the draw order is working in this situation):
If I call InvalidateRect() and pass the child handle, the child window is indeed redrawn, but the background is not redrawn, and so the pixels accumulate on top of each other, blending together.
If I call InvalidateRect() and pass in the parent handle, with a rectangle consisting of the child window's dimensions, the background is redrawn, but the child window is not.
If I do both of the above, then the background is redrawn as well as the child window, and it looks exactly as I'd want -- except that by doing so, I've managed to make it flicker again (which isn't really surprising, since it seems terribly hackish to call InvalidateRect() twice like that, as I'd guess that each call is probably causing the buffers to flip, which defeats the purpose).
What I've come to conclude is that I don't really understand how I need to modify my program to handle double buffering, or if double buffering will even help with this situation. I feel like it definitely would, but I don't quite understand how I need to modify things to get everything to play nicely again.
Are you using layered windows? If not maybe try it out.
Also for double buffering consider this technique.
This could be completely off base - my gui days were a long long time ago...
But couldn't you just precompute the blends for your different states? I assume your button can be enabled/disable and up/down, so that's only 4 combinations. Why not precompute the combined bitmaps?
Or is the problem the interaction of already combined bitmaps with the existing state?