I am using C++11 with GNU tool chain with gtkmm3, on Ubuntu 12.04 LTS 32 bit. I have been experimenting with some of the examples for gtkmm3 in Programming with gtkmm 3.
Based on 17.2.1.Example there, I inherited from Gtk::DrawingArea
(MyDrawingArea
here) and overrode the on_draw()
event handler as follows:
MyDrawingArea.hpp
...
protected:
bool on_draw ( const Cairo::RefPtr<Cairo::Context>& cr ) override;
MyDrawingArea.cpp (quick and dirty, just for concept demo)
double y{ 10 };
double x{ 10 };
bool MyDrawingArea::on_draw( const Cairo::RefPtr<Cairo::Context>& cr )
{
this->get_window( )->freeze_updates( );
cr->set_line_width( 3.0 );
cr->set_source_rgb( 1, 0, 0 );
cr->move_to( x, y );
cr->line_to( x * 2, y * 2 );
cr->stroke( );
this->get_window( )->thaw_updates( );
x += 50;
y += 50;
return true;
}
This code draws a single diagonal line. I want its position to change when I callMyDrawingArea.queue_draw()
through an event handler in my application. This works:
Event 1: Event 2:
My problem is that, obviously, the on_draw()
event handler fires every time the window is repainted. Simply moving the application's main window (which contains MyDrawingArea
) causes on_draw()
to fire and the line is rendered in a new position.
How do I control when the code in my on_draw()
event will run, so that the line is rendered anew only when I call MyDrawingArea.queue_draw()
in my application code, but is preserved in its previous state at other times? (I don't think I'm asking about how to prevent on_draw()
from firing, but perhaps that's what has to happen?)
Simply setting a flag notifying that my event has made the call and only then running my code to render the line raises other problems, because when the flag is not set, I lose everything that was previously rendered.
This seems to be an impossible situation: Either the line is redrawn in a new position for every on_draw()
event, or erased completely if I use a flag to invoke my drawing logic, and on_draw()
fires when that flag isn't set.
How should I manage this? Do I need logic to manage two different on_draw()
options: If the app makes the call, render the new version of the line, if not, repaint the old version? This gets complicated - I'm thinking I'm missing something. Is there a different event I should be using? Is the some way I can obtain from Cairo::Context
information about who called On_draw()
, etc that would help me negotiate this issue?
I solved this problem by writing a little state machine that controls how the on_draw()
event behaves:
Enum representing different signal states when on on_draw()
fires:
enum class Draw_Signals
{
DS_RedrawSignal, DS_HoldSignal, DS_ClearSignal, DS_End
};
DS_RedrawSignal
Means redraw with new additions/changes as per the most recent application activity.
DS_HoldSignal
Means maintain the current graphics state but do nothing new.
DS_ClearSignal
Means clear the drawing area entirely and start over.
The MyDrawingArea
class has a member:
Draw_Signals mDSignal { Draw_Signals::DS_HoldSignal };
This value is set by methods that triggeron_draw()
from my application with a "setter" method.
Without going into all the details, my MyDrawingArea::on_draw()
now looks something like this:
bool MyDrawingArea::on_draw( const Cairo::RefPtr<Cairo::Context>& cr )
{
get_window( )->freeze_updates( );
switch ( mDSignal )
{
case Draw_Signals::DS_RedrawSignal:
{
DrawNewLine( cr );//draws new line and caches it for use when RedrawOldLine() is called.
break;
}
case Draw_Signals::DS_HoldSignal:
{
RedrawOldLine( cr );
break;
}
case Draw_Signals::DS_ClearSignal:
{
clearDrawArea( );
}
}
get_window( )->thaw_updates( );
mDSignal = Draw_Signals::DS_HoldSignal;
return true;
}
They key here is that the default state is always Draw_Signals::DS_HoldSignal,
which simply redraws the previous content, and new drawing only occurs when explicitly signaled otherwise from the application.