File indexing completed on 2024-05-12 17:05:51
0001 /* 0002 this file is part of the oxygen gtk engine 0003 SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "oxygentoolbarstatedata.h" 0009 #include "../oxygengtkutils.h" 0010 #include "../config.h" 0011 0012 #include <gtk/gtk.h> 0013 0014 namespace Oxygen 0015 { 0016 0017 //________________________________________________________________________________ 0018 const int ToolBarStateData::_timeOut = 50; 0019 void ToolBarStateData::connect( GtkWidget* widget ) 0020 { 0021 0022 #if OXYGEN_DEBUG 0023 std::cerr 0024 << "Oxygen::ToolBarStateData::connect - " 0025 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0026 << std::endl; 0027 #endif 0028 0029 // store widget 0030 _target = widget; 0031 0032 // connect signals 0033 _leaveId.connect( G_OBJECT(widget), "leave-notify-event", G_CALLBACK( leaveNotifyEvent ), this ); 0034 0035 // connect timeLines 0036 _current._timeLine.connect( (GSourceFunc)delayedUpdate, this ); 0037 _previous._timeLine.connect( (GSourceFunc)delayedUpdate, this ); 0038 0039 // set directions 0040 _current._timeLine.setDirection( TimeLine::Forward ); 0041 _previous._timeLine.setDirection( TimeLine::Backward ); 0042 0043 // follow mouse animation 0044 FollowMouseData::connect( (GSourceFunc)followMouseUpdate, this ); 0045 0046 } 0047 0048 //________________________________________________________________________________ 0049 void ToolBarStateData::disconnect( GtkWidget* widget ) 0050 { 0051 0052 #if OXYGEN_DEBUG 0053 std::cerr 0054 << "Oxygen::ToolBarStateData::disconnect - " 0055 << " " << _target << " (" << (_target ? G_OBJECT_TYPE_NAME( _target ) : "0x0") << ")" 0056 << std::endl; 0057 #endif 0058 0059 _target = 0L; 0060 0061 // disconnect signal 0062 _leaveId.disconnect(); 0063 0064 // disconnect timelines 0065 _current._timeLine.disconnect(); 0066 _previous._timeLine.disconnect(); 0067 _timer.stop(); 0068 0069 // disconnect all children 0070 for( HoverDataMap::iterator iter = _hoverData.begin(); iter != _hoverData.end(); ++iter ) 0071 { iter->second.disconnect(); } 0072 0073 _hoverData.clear(); 0074 0075 FollowMouseData::disconnect(); 0076 0077 } 0078 0079 //________________________________________________________________________________ 0080 void ToolBarStateData::registerChild( GtkWidget* widget, bool value ) 0081 { 0082 0083 // make sure widget is not already in map 0084 if( _hoverData.find( widget ) == _hoverData.end() ) 0085 { 0086 0087 #if OXYGEN_DEBUG 0088 std::cerr 0089 << "Oxygen::ToolBarStateData::registerChild -" 0090 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0091 << std::endl; 0092 #endif 0093 0094 // allocate new Hover data 0095 HoverData data; 0096 data._destroyId.connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this ); 0097 data._enterId.connect( G_OBJECT(widget), "enter-notify-event", G_CALLBACK( childEnterNotifyEvent ), this ); 0098 data._leaveId.connect( G_OBJECT(widget), "leave-notify-event", G_CALLBACK( childLeaveNotifyEvent ), this ); 0099 0100 // and insert in map 0101 _hoverData.insert( std::make_pair( widget, data ) ); 0102 updateState( widget, value, false ); 0103 0104 0105 } 0106 0107 } 0108 0109 //________________________________________________________________________________ 0110 void ToolBarStateData::unregisterChild( GtkWidget* widget ) 0111 { 0112 0113 #if OXYGEN_DEBUG 0114 std::cerr 0115 << "Oxygen::ToolBarStateData::unregisterChild -" 0116 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0117 << std::endl; 0118 #endif 0119 0120 // loopup in hover map 0121 HoverDataMap::iterator iter( _hoverData.find( widget ) ); 0122 if( iter != _hoverData.end() ) 0123 { 0124 iter->second.disconnect(); 0125 _hoverData.erase( iter ); 0126 } 0127 0128 // disconnect previous and current 0129 if( widget == _previous._widget ) 0130 { 0131 _previous._widget = 0L; 0132 _previous._timeLine.disconnect(); 0133 } 0134 0135 if( widget == _current._widget ) 0136 { 0137 _current._widget = 0L; 0138 _current._timeLine.disconnect(); 0139 } 0140 0141 } 0142 0143 //________________________________________________________________________________ 0144 bool ToolBarStateData::updateState( GtkWidget* widget, bool state, bool delayed ) 0145 { 0146 0147 const GdkRectangle rect( widget ? Gtk::gtk_widget_get_allocation( widget ) : Gtk::gdk_rectangle() ); 0148 0149 if( state && widget != _current._widget ) 0150 { 0151 0152 // stop timer 0153 if( _timer.isRunning() ) _timer.stop(); 0154 0155 // stop current animation if running 0156 if( _current._timeLine.isRunning() ) _current._timeLine.stop(); 0157 0158 // stop previous animation if running 0159 if( _current.isValid() ) 0160 { 0161 if( _previous._timeLine.isRunning() ) _previous._timeLine.stop(); 0162 0163 if( _previous.isValid() ) 0164 { _dirtyRect = _previous._rect; } 0165 0166 // move current to previous 0167 _previous.copy( _current ); 0168 } 0169 0170 // assign new widget to current and start animation 0171 const GdkRectangle startRect( _current._rect ); 0172 const bool animate( !_current.isValid() ); 0173 _current.update( widget, rect ); 0174 if( _current.isValid() ) 0175 { 0176 if( animate || !followMouse() ) _current._timeLine.start(); 0177 else startAnimation( startRect, _current._rect ); 0178 0179 } 0180 0181 return true; 0182 0183 } else if( (!state) && widget == _current._widget ) { 0184 0185 // stop current animation if running 0186 if( _current._timeLine.isRunning() ) _current._timeLine.stop(); 0187 0188 // stop previous animation if running 0189 if( _previous._timeLine.isRunning() ) _previous._timeLine.stop(); 0190 0191 if( _previous.isValid() ) 0192 { _dirtyRect = _previous._rect; } 0193 0194 // move current to previous; clear current, and animate 0195 if( followMouse() && delayed ) { 0196 0197 // start delayed animation 0198 if( !_timer.isRunning() ) 0199 { _timer.start( _timeOut, (GSourceFunc)delayedAnimate, this ); } 0200 0201 } else { 0202 0203 if( _timer.isRunning() ) _timer.stop(); 0204 _previous.copy( _current ); 0205 _current.clear(); 0206 0207 // start fade-out timeLine 0208 if( _previous.isValid() ) _previous._timeLine.start(); 0209 0210 } 0211 0212 return true; 0213 0214 } else return false; 0215 0216 } 0217 0218 //_____________________________________________ 0219 GdkRectangle ToolBarStateData::dirtyRect( void ) 0220 { 0221 0222 GdkRectangle rect( Gtk::gdk_rectangle() ); 0223 Gtk::gdk_rectangle_union( &_previous._rect, &_current._rect, &rect ); 0224 0225 // add _dirtyRect 0226 if( Gtk::gdk_rectangle_is_valid( &_dirtyRect ) ) 0227 { 0228 Gtk::gdk_rectangle_union( &_dirtyRect, &rect, &rect ); 0229 _dirtyRect = Gtk::gdk_rectangle(); 0230 } 0231 0232 // add followMouse dirtyRect 0233 if( followMouse() ) 0234 { 0235 const GdkRectangle followMouseRect( FollowMouseData::dirtyRect() ); 0236 Gtk::gdk_rectangle_union( &followMouseRect, &rect, &rect ); 0237 } 0238 0239 // check validity 0240 if( !Gtk::gdk_rectangle_is_valid( &rect ) ) return rect; 0241 0242 /* Add viewport offsets if any */ 0243 if( GTK_IS_VIEWPORT( _target ) ) 0244 { 0245 gint xOffset, yOffset; 0246 Gtk::gtk_viewport_get_position( GTK_VIEWPORT( _target ), &xOffset, &yOffset ); 0247 rect.x -= xOffset; 0248 rect.y -= yOffset; 0249 } 0250 0251 /* Add extra margin to rect. FIXME: this should be triggered from widget's metrics */ 0252 rect.x -= 2; 0253 rect.y -= 2; 0254 rect.width += 4; 0255 rect.height += 4; 0256 0257 return rect; 0258 0259 } 0260 0261 //____________________________________________________________________________________________ 0262 gboolean ToolBarStateData::childDestroyNotifyEvent( GtkWidget* widget, gpointer data ) 0263 { 0264 #if OXYGEN_DEBUG 0265 std::cerr 0266 << "Oxygen::ToolBarStateData::childDestroyNotifyEvent -" 0267 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0268 << std::endl; 0269 #endif 0270 static_cast<ToolBarStateData*>(data)->unregisterChild( widget ); 0271 return FALSE; 0272 } 0273 0274 //________________________________________________________________________________ 0275 gboolean ToolBarStateData::childEnterNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data ) 0276 { 0277 0278 #if OXYGEN_DEBUG 0279 std::cerr 0280 << "Oxygen::ToolBarStateData::childEnterNotifyEvent -" 0281 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0282 << std::endl; 0283 #endif 0284 0285 static_cast<ToolBarStateData*>( data )->updateState( widget, true, false ); 0286 return FALSE; 0287 } 0288 0289 //________________________________________________________________________________ 0290 gboolean ToolBarStateData::childLeaveNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data ) 0291 { 0292 0293 #if OXYGEN_DEBUG 0294 std::cerr 0295 << "Oxygen::ToolBarStateData::childLeaveNotifyEvent -" 0296 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0297 << std::endl; 0298 #endif 0299 0300 static_cast<ToolBarStateData*>( data )->updateState( widget, false, true ); 0301 return FALSE; 0302 0303 } 0304 0305 //________________________________________________________________________________ 0306 gboolean ToolBarStateData::leaveNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer pointer ) 0307 { 0308 #if OXYGEN_DEBUG 0309 std::cerr 0310 << "Oxygen::ToolBarStateData::leaveNotifyEvent -" 0311 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0312 << std::endl; 0313 #endif 0314 0315 ToolBarStateData& data( *static_cast<ToolBarStateData*>( pointer ) ); 0316 if( data._current.isValid() ) data.updateState( data._current._widget, false, false ); 0317 return FALSE; 0318 } 0319 0320 //_____________________________________________ 0321 gboolean ToolBarStateData::delayedUpdate( gpointer pointer ) 0322 { 0323 0324 ToolBarStateData& data( *static_cast<ToolBarStateData*>( pointer ) ); 0325 0326 if( data._target ) 0327 { 0328 const GdkRectangle rect( data.dirtyRect() ); 0329 Gtk::gtk_widget_queue_draw( data._target, &rect ); 0330 if( data._previous._widget ) Gtk::gtk_widget_queue_draw( data._previous._widget ); 0331 if( data._current._widget ) Gtk::gtk_widget_queue_draw( data._current._widget ); 0332 } 0333 0334 return FALSE; 0335 0336 } 0337 0338 //_____________________________________________ 0339 gboolean ToolBarStateData::followMouseUpdate( gpointer pointer ) 0340 { 0341 0342 ToolBarStateData& data( *static_cast<ToolBarStateData*>( pointer ) ); 0343 0344 if( data._target && data.followMouse() ) 0345 { 0346 0347 data.updateAnimatedRect(); 0348 0349 GdkRectangle rect( data.dirtyRect() ); 0350 Gtk::gtk_widget_queue_draw( data._target, &rect ); 0351 if( data._previous._widget ) Gtk::gtk_widget_queue_draw( data._previous._widget ); 0352 if( data._current._widget ) Gtk::gtk_widget_queue_draw( data._current._widget ); 0353 0354 } 0355 0356 return FALSE; 0357 0358 } 0359 0360 //_____________________________________________ 0361 gboolean ToolBarStateData::delayedAnimate( gpointer pointer ) 0362 { 0363 0364 ToolBarStateData& data( *static_cast<ToolBarStateData*>( pointer ) ); 0365 data._previous.copy( data._current ); 0366 data._current.clear(); 0367 0368 if( data._previous.isValid() ) 0369 { data._previous._timeLine.start(); } 0370 0371 return FALSE; 0372 0373 } 0374 }