File indexing completed on 2024-05-12 05:34:34
0001 /* 0002 this file is part of the oxygen gtk engine 0003 SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr> 0004 SPDX-FileCopyrightText: 2010 Ruslan Kabatsayev <b7.10110111@gmail.com> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "oxygeninnershadowdata.h" 0010 #include "../oxygengtkutils.h" 0011 #include "../config.h" 0012 #include "../oxygencairocontext.h" 0013 #include "../oxygencairoutils.h" 0014 #include "oxygenanimations.h" 0015 #include "../oxygenstyle.h" 0016 #include "../oxygenmetrics.h" 0017 0018 #include <gtk/gtk.h> 0019 #include <cstdlib> 0020 0021 #include <cassert> 0022 #include <iostream> 0023 0024 namespace Oxygen 0025 { 0026 0027 //_____________________________________________ 0028 void InnerShadowData::connect( GtkWidget* widget ) 0029 { 0030 0031 assert( GTK_IS_SCROLLED_WINDOW( widget ) ); 0032 assert( !_target ); 0033 0034 // store target 0035 _target = widget; 0036 0037 if( gdk_display_supports_composite( gtk_widget_get_display( widget ) ) ) 0038 { _exposeId.connect( G_OBJECT(_target), "expose-event", G_CALLBACK( targetExposeEvent ), this, true ); } 0039 0040 // check child 0041 GtkWidget* child( gtk_bin_get_child( GTK_BIN( widget ) ) ); 0042 if( !child ) return; 0043 0044 #if OXYGEN_DEBUG 0045 std::cerr 0046 << "Oxygen::InnerShadowData::connect -" 0047 << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0048 << " child: " << child << " (" << G_OBJECT_TYPE_NAME( child ) << ")" 0049 << std::endl; 0050 #endif 0051 0052 registerChild( child ); 0053 0054 } 0055 0056 //_____________________________________________ 0057 void InnerShadowData::disconnect( GtkWidget* ) 0058 { 0059 _target = 0; 0060 for( ChildDataMap::reverse_iterator iter = _childrenData.rbegin(); iter != _childrenData.rend(); ++iter ) 0061 { iter->second.disconnect( iter->first ); } 0062 0063 // disconnect signals 0064 _exposeId.disconnect(); 0065 0066 // clear child data 0067 _childrenData.clear(); 0068 } 0069 0070 //_____________________________________________ 0071 void InnerShadowData::registerChild( GtkWidget* widget ) 0072 { 0073 0074 #if ENABLE_INNER_SHADOWS_HACK 0075 0076 // make sure widget is not already in map 0077 if( _childrenData.find( widget ) != _childrenData.end() ) return; 0078 0079 if( gtk_scrolled_window_get_shadow_type( GTK_SCROLLED_WINDOW( _target ) ) != GTK_SHADOW_IN ) 0080 { return; } 0081 0082 #if OXYGEN_DEBUG 0083 std::cerr 0084 << "Oxygen::InnerShadowData::registerChild -" 0085 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0086 << std::endl; 0087 #endif 0088 0089 GdkWindow* window(gtk_widget_get_window(widget)); 0090 if( 0091 0092 // check window 0093 window && 0094 gdk_window_get_window_type( window ) == GDK_WINDOW_CHILD && 0095 0096 // check compositing 0097 gdk_display_supports_composite( gtk_widget_get_display( widget ) ) && 0098 0099 // make sure widget is scrollable 0100 GTK_WIDGET_GET_CLASS( widget )->set_scroll_adjustments_signal ) 0101 { 0102 ChildData data; 0103 data._unrealizeId.connect( G_OBJECT(widget), "unrealize", G_CALLBACK( childUnrealizeNotifyEvent ), this ); 0104 data._initiallyComposited = gdk_window_get_composited(window); 0105 gdk_window_set_composited(window,TRUE); 0106 _childrenData.insert( std::make_pair( widget, data ) ); 0107 } 0108 0109 #endif 0110 0111 } 0112 0113 //________________________________________________________________________________ 0114 void InnerShadowData::unregisterChild( GtkWidget* widget ) 0115 { 0116 #if ENABLE_INNER_SHADOWS_HACK 0117 0118 ChildDataMap::iterator iter( _childrenData.find( widget ) ); 0119 if( iter == _childrenData.end() ) return; 0120 0121 #if OXYGEN_DEBUG 0122 std::cerr 0123 << "Oxygen::InnerShadowData::unregisterChild -" 0124 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0125 << std::endl; 0126 #endif 0127 0128 iter->second.disconnect( widget ); 0129 _childrenData.erase( iter ); 0130 0131 #endif 0132 } 0133 0134 //________________________________________________________________________________ 0135 void InnerShadowData::ChildData::disconnect( GtkWidget* widget ) 0136 { 0137 #if ENABLE_INNER_SHADOWS_HACK 0138 0139 // disconnect signals 0140 _unrealizeId.disconnect(); 0141 0142 // remove compositing flag 0143 GdkWindow* window( gtk_widget_get_window( widget ) ); 0144 0145 #if OXYGEN_DEBUG 0146 std::cerr 0147 << "Oxygen::InnerShadowData::ChildData::disconnect -" 0148 << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0149 << " window: " << window 0150 << std::endl; 0151 #endif 0152 0153 // restore compositing if different from initial state 0154 if( GDK_IS_WINDOW( window ) && !gdk_window_is_destroyed(window) && gdk_window_get_composited( window ) != _initiallyComposited ) 0155 { gdk_window_set_composited( window, _initiallyComposited ); } 0156 0157 #endif 0158 } 0159 0160 //____________________________________________________________________________________________ 0161 gboolean InnerShadowData::childUnrealizeNotifyEvent( GtkWidget* widget, gpointer data ) 0162 { 0163 #if OXYGEN_DEBUG 0164 std::cerr 0165 << "Oxygen::InnerShadowData::childUnrealizeNotifyEvent -" 0166 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0167 << std::endl; 0168 #endif 0169 static_cast<InnerShadowData*>(data)->unregisterChild( widget ); 0170 return FALSE; 0171 } 0172 0173 //_________________________________________________________________________________________ 0174 gboolean InnerShadowData::targetExposeEvent( GtkWidget* widget, GdkEventExpose* event, gpointer ) 0175 { 0176 0177 #if ENABLE_INNER_SHADOWS_HACK 0178 GtkWidget* child=gtk_bin_get_child(GTK_BIN(widget)); 0179 GdkWindow* window=gtk_widget_get_window(child); 0180 0181 #if OXYGEN_DEBUG 0182 std::cerr << "Oxygen::InnerShadowData::targetExposeEvent -" 0183 << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME(widget) << ")" 0184 << " child: " << child << " (" << G_OBJECT_TYPE_NAME(child) << ")" 0185 << " path: " << Gtk::gtk_widget_path( child ) 0186 << " area: " << event->area 0187 << std::endl; 0188 #endif 0189 0190 if(!gdk_window_get_composited(window)) 0191 { 0192 #if OXYGEN_DEBUG 0193 std::cerr << "Oxygen::InnerShadowData::targetExposeEvent - Window isn't composite. Doing nohing\n"; 0194 #endif 0195 return FALSE; 0196 } 0197 0198 // make sure the child window doesn't contain garbage 0199 gdk_window_process_updates(window,TRUE); 0200 0201 // get window geometry 0202 GtkAllocation allocation( Gtk::gdk_rectangle() ); 0203 gdk_window_get_geometry( window, &allocation.x, &allocation.y, &allocation.width, &allocation.height, 0L ); 0204 0205 // create context with clipping 0206 Cairo::Context context(gtk_widget_get_window(widget), &allocation ); 0207 0208 // add event region 0209 gdk_cairo_region(context,event->region); 0210 cairo_clip(context); 0211 0212 // draw child 0213 gdk_cairo_set_source_window( context, window, allocation.x, allocation.y ); 0214 cairo_paint(context); 0215 0216 #if OXYGEN_DEBUG_INNERSHADOWS 0217 // Show updated parts in random color 0218 cairo_rectangle(context,allocation.x,allocation.y,allocation.width,allocation.height); 0219 double red=((double)rand())/RAND_MAX; 0220 double green=((double)rand())/RAND_MAX; 0221 double blue=((double)rand())/RAND_MAX; 0222 cairo_set_source_rgba(context,red,green,blue,0.5); 0223 cairo_fill(context); 0224 #endif 0225 0226 // Render rounded combobox list child 0227 if(Gtk::gtk_combobox_is_tree_view( child )) 0228 { 0229 StyleOptions options(widget,gtk_widget_get_state(widget)); 0230 Corners corners(CornersAll); 0231 if(gtk_widget_get_visible(gtk_scrolled_window_get_vscrollbar(GTK_SCROLLED_WINDOW(widget)))) 0232 { 0233 if(Gtk::gtk_widget_layout_is_reversed( widget )) 0234 corners &= ~CornersLeft; 0235 else 0236 corners &= ~CornersRight; 0237 } 0238 if(gtk_widget_get_visible(gtk_scrolled_window_get_hscrollbar(GTK_SCROLLED_WINDOW(widget)))) 0239 corners &= ~CornersBottom; 0240 0241 int x(allocation.x),y(allocation.y),w(allocation.width),h(allocation.height); 0242 cairo_rectangle(context,x,y,w,h); 0243 if(!Gtk::gdk_default_screen_is_composited()) 0244 { 0245 // Take ugly shadow into account 0246 x+=1; 0247 y+=1; 0248 w-=2; 0249 h-=2; 0250 } 0251 cairo_rounded_rectangle_negative(context,x,y,w,h,2,corners); 0252 cairo_clip(context); 0253 0254 Style::instance().renderMenuBackground( gtk_widget_get_window(widget), context, allocation.x,allocation.y,allocation.width,allocation.height, options ); 0255 0256 // Event handling finished, now let the event propagate 0257 return FALSE; 0258 } 0259 0260 // draw the shadow 0261 /* 0262 TODO: here child widget's allocation is used instead of window geometry. 0263 I think this is the correct thing to do (unlike above), but this is to be double check 0264 */ 0265 allocation = Gtk::gtk_widget_get_allocation( child ); 0266 int basicOffset=2; 0267 0268 // we only draw SHADOW_IN here 0269 if( gtk_scrolled_window_get_shadow_type( GTK_SCROLLED_WINDOW( widget ) ) != GTK_SHADOW_IN ) 0270 { 0271 if( GTK_IS_VIEWPORT(child) && gtk_viewport_get_shadow_type(GTK_VIEWPORT(child)) == GTK_SHADOW_IN ) 0272 { 0273 0274 basicOffset=0; 0275 0276 } else { 0277 0278 // FIXME: do we need this special case? 0279 // special_case { 0280 // we still want to draw shadow on GtkFrames with shadow containing GtkScrolledWindow without shadow 0281 GtkWidget* box=gtk_widget_get_parent(widget); 0282 GtkWidget* frame=0; 0283 if(GTK_IS_BOX(box) && GTK_IS_FRAME(frame=gtk_widget_get_parent(box)) && 0284 gtk_frame_get_shadow_type(GTK_FRAME(frame))==GTK_SHADOW_IN) 0285 { 0286 #if OXYGEN_DEBUG 0287 std::cerr << "Oxygen::InnerShadowData::targetExposeEvent: Box children: " << GTK_CONTAINER(box) << std::endl; 0288 #endif 0289 // make sure GtkScrolledWindow is the only visible child 0290 GList* children=gtk_container_get_children(GTK_CONTAINER(box)); 0291 for(GList* child=g_list_first(children); child; child=g_list_next(child)) 0292 { 0293 GtkWidget* childWidget(GTK_WIDGET(child->data)); 0294 if(gtk_widget_get_visible(childWidget) && !GTK_IS_SCROLLED_WINDOW(childWidget)) 0295 { 0296 g_list_free(children); 0297 return FALSE; 0298 } 0299 } 0300 int frameX, frameY; 0301 GtkAllocation frameAlloc; 0302 if(gtk_widget_translate_coordinates(frame,widget,0,0,&frameX,&frameY)) 0303 { 0304 #if OXYGEN_DEBUG 0305 std::cerr << "coords translation: x=" << frameX << "; y=" << frameY << std::endl; 0306 #endif 0307 gtk_widget_get_allocation(frame,&frameAlloc); 0308 allocation.x+=frameX; 0309 allocation.y+=frameY; 0310 allocation.width=frameAlloc.width; 0311 allocation.height=frameAlloc.height; 0312 basicOffset=0; 0313 } 0314 0315 } else { 0316 0317 #if OXYGEN_DEBUG 0318 std::cerr << "Oxygen::InnerShadowData::targetExposeEvent - Shadow type isn't GTK_SHADOW_IN, so not drawing the shadow in expose-event handler\n"; 0319 #endif 0320 return FALSE; 0321 0322 } 0323 0324 } 0325 } 0326 0327 StyleOptions options(widget,gtk_widget_get_state(widget)); 0328 options|=NoFill; 0329 options &= ~(Hover|Focus); 0330 if( Style::instance().animations().scrolledWindowEngine().contains( widget ) ) 0331 { 0332 if( Style::instance().animations().scrolledWindowEngine().focused( widget ) ) options |= Focus; 0333 if( Style::instance().animations().scrolledWindowEngine().hovered( widget ) ) options |= Hover; 0334 } 0335 0336 const AnimationData data( Style::instance().animations().widgetStateEngine().get( widget, options, AnimationHover|AnimationFocus, AnimationFocus ) ); 0337 0338 int offsetX=basicOffset+Entry_SideMargin; 0339 int offsetY=basicOffset; 0340 0341 // clipRect 0342 GdkRectangle clipRect( allocation ); 0343 0344 // hole background 0345 Style::instance().renderHoleBackground( 0346 gtk_widget_get_window(widget), widget, &clipRect, 0347 allocation.x-offsetX, allocation.y-offsetY, allocation.width+offsetX*2, allocation.height+offsetY*2 ); 0348 0349 // adjust offset and render hole 0350 offsetX -= Entry_SideMargin; 0351 Style::instance().renderHole( 0352 gtk_widget_get_window(widget), &clipRect, 0353 allocation.x-offsetX, allocation.y-offsetY, allocation.width+offsetX*2, allocation.height+offsetY*2, 0354 options, data ); 0355 0356 #endif // enable inner shadows hack 0357 0358 // let the event propagate 0359 return FALSE; 0360 } 0361 0362 }