From 11c91c717942d385d72295302682e41c16d93ae9 Mon Sep 17 00:00:00 2001 From: Jeff Young Date: Sun, 3 Oct 2021 13:27:15 +0100 Subject: [PATCH] Improve obscuring dialog algorithm to handle multiple dialogs. Fixes https://gitlab.com/kicad/code/kicad/issues/8970 --- common/eda_draw_frame.cpp | 40 +++++++++++++++-------------------- common/view/view.cpp | 44 ++++++++++++++++++--------------------- include/eda_draw_frame.h | 2 +- include/view/view.h | 6 +++--- pcbnew/pcb_base_frame.cpp | 9 +++++--- 5 files changed, 47 insertions(+), 54 deletions(-) diff --git a/common/eda_draw_frame.cpp b/common/eda_draw_frame.cpp index 104ccced3c..c2ff08ff74 100644 --- a/common/eda_draw_frame.cpp +++ b/common/eda_draw_frame.cpp @@ -836,15 +836,17 @@ void EDA_DRAW_FRAME::Zoom_Automatique( bool aWarpPointer ) // Find the first child dialog. -wxWindow* EDA_DRAW_FRAME::findDialog() +std::vector EDA_DRAW_FRAME::findDialogs() { + std::vector dialogs; + for( wxWindow* window : GetChildren() ) { if( dynamic_cast( window ) ) - return window; + dialogs.push_back( window ); } - return nullptr; + return dialogs; } @@ -859,33 +861,25 @@ void EDA_DRAW_FRAME::FocusOnLocation( const wxPoint& aPos ) if( !r.Contains( aPos ) ) centerView = true; - // Center if we're behind an obscuring dialog, or within 10% of its edge - wxWindow* dialog = findDialog(); + std::vector dialogScreenRects; - if( dialog ) + for( wxWindow* dialog : findDialogs() ) { - wxRect dialogRect( GetCanvas()->ScreenToClient( dialog->GetScreenPosition() ), - dialog->GetSize() ); - dialogRect.Inflate( dialogRect.GetWidth() / 10 ); + dialogScreenRects.emplace_back( GetCanvas()->ScreenToClient( dialog->GetScreenPosition() ), + dialog->GetSize() ); + } - if( dialogRect.Contains( (wxPoint) GetCanvas()->GetView()->ToScreen( aPos ) ) ) + // Center if we're behind an obscuring dialog, or within 10% of its edge + for( BOX2D rect : dialogScreenRects ) + { + rect.Inflate( rect.GetWidth() / 10 ); + + if( rect.Contains( GetCanvas()->GetView()->ToScreen( aPos ) ) ) centerView = true; } if( centerView ) - { - // If a dialog partly obscures the window, then center on the uncovered area. - if( dialog ) - { - BOX2D dialogRect( GetCanvas()->ScreenToClient( dialog->GetScreenPosition() ), - dialog->GetSize() ); - GetCanvas()->GetView()->SetCenter( aPos, dialogRect ); - } - else - { - GetCanvas()->GetView()->SetCenter( aPos ); - } - } + GetCanvas()->GetView()->SetCenter( aPos, dialogScreenRects ); GetCanvas()->GetViewControls()->SetCrossHairCursorPosition( aPos ); } diff --git a/common/view/view.cpp b/common/view/view.cpp index 217dc90f5b..ac9b4d371d 100644 --- a/common/view/view.cpp +++ b/common/view/view.cpp @@ -603,39 +603,35 @@ void VIEW::SetCenter( const VECTOR2D& aCenter ) } -void VIEW::SetCenter( const VECTOR2D& aCenter, const BOX2D& occultingScreenRect ) +void VIEW::SetCenter( const VECTOR2D& aCenter, const std::vector& obscuringScreenRects ) { - VECTOR2D center( aCenter ); - BOX2D screenRect( VECTOR2D( 0, 0 ), m_gal->GetScreenPixelSize() ); + if( obscuringScreenRects.empty() ) + return SetCenter( aCenter ); - if( !screenRect.Intersects( occultingScreenRect ) ) + BOX2D screenRect( { 0, 0 }, m_gal->GetScreenPixelSize() ); + SHAPE_POLY_SET unobscuredPoly( screenRect ); + VECTOR2D unobscuredCenter = screenRect.Centre(); + + for( const BOX2D& obscuringScreenRect : obscuringScreenRects ) { - SetCenter( aCenter ); - return; + SHAPE_POLY_SET obscuringPoly( obscuringScreenRect ); + unobscuredPoly.BooleanSubtract( obscuringPoly, SHAPE_POLY_SET::PM_FAST ); } - BOX2D occultedRect = screenRect.Intersect( occultingScreenRect ); - double topExposed = occultedRect.GetTop() - screenRect.GetTop(); - double bottomExposed = screenRect.GetBottom() - occultedRect.GetBottom(); - double leftExposed = occultedRect.GetLeft() - screenRect.GetLeft(); - double rightExposed = screenRect.GetRight() - occultedRect.GetRight(); + /* + * Perform a step-wise deflate to find the center of the largest unobscured area + */ - if( std::max( topExposed, bottomExposed ) > std::max( leftExposed, rightExposed ) ) + BOX2I bbox = unobscuredPoly.BBox(); + int step = std::min( bbox.GetWidth(), bbox.GetHeight() ) / 10; + + while( !unobscuredPoly.IsEmpty() ) { - if( topExposed > bottomExposed ) - center.y += ToWorld( screenRect.GetHeight() / 2 - topExposed / 2 ); - else - center.y -= ToWorld( screenRect.GetHeight() / 2 - bottomExposed / 2 ); - } - else - { - if( leftExposed > rightExposed ) - center.x += ToWorld( screenRect.GetWidth() / 2 - leftExposed / 2 ); - else - center.x -= ToWorld( screenRect.GetWidth() / 2 - rightExposed / 2 ); + unobscuredCenter = (wxPoint) unobscuredPoly.BBox().Centre(); + unobscuredPoly.Deflate( step, 4 ); } - SetCenter( center ); + SetCenter( aCenter - ToWorld( unobscuredCenter - screenRect.Centre(), false ) ); } diff --git a/include/eda_draw_frame.h b/include/eda_draw_frame.h index a0739e0691..c0450e8a36 100644 --- a/include/eda_draw_frame.h +++ b/include/eda_draw_frame.h @@ -462,7 +462,7 @@ protected: void setupUnits( APP_SETTINGS_BASE* aCfg ); - wxWindow* findDialog(); + std::vector findDialogs(); /** * Determines the Canvas type to load (with prompt if required) and initializes m_canvasType diff --git a/include/view/view.h b/include/view/view.h index f73c2df3c0..60bd2e0304 100644 --- a/include/view/view.h +++ b/include/view/view.h @@ -318,13 +318,13 @@ public: void SetCenter( const VECTOR2D& aCenter ); /** - * Set the center point of the VIEW, attempting to avoid \a occultingScreenRect (for + * Set the center point of the VIEW, attempting to avoid \a obscuringScreenRects (for * instance, the screen rect of a modeless dialog in front of the VIEW). * * @param aCenter: the new center point, in world space coordinates. - * @param occultingScreenRect: the occulting rect, in screen space coordinates. + * @param obscuringScreenRects: the obscuring rects, in screen space coordinates. */ - void SetCenter( const VECTOR2D& aCenter, const BOX2D& occultingScreenRect ); + void SetCenter( const VECTOR2D& aCenter, const std::vector& obscuringScreenRects ); /** * Return the center point of this VIEW (in world space coordinates). diff --git a/pcbnew/pcb_base_frame.cpp b/pcbnew/pcb_base_frame.cpp index 4d79dcc626..e2c50a0783 100644 --- a/pcbnew/pcb_base_frame.cpp +++ b/pcbnew/pcb_base_frame.cpp @@ -271,15 +271,14 @@ void PCB_BASE_FRAME::FocusOnItem( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer ) GetCanvas()->GetView()->Update( aItem ); lastBrightenedItemID = aItem->m_Uuid; - // Focus on the object's location. Prefer a visible part of the object to its anhcor + // Focus on the object's location. Prefer a visible part of the object to its anchor // in order to keep from scrolling around. wxPoint focusPt = aItem->GetFocusPosition(); KIGFX::VIEW* view = GetCanvas()->GetView(); SHAPE_POLY_SET viewportPoly( view->GetViewport() ); - wxWindow* dialog = findDialog(); - if( dialog ) + for( wxWindow* dialog : findDialogs() ) { wxPoint dialogPos = GetCanvas()->ScreenToClient( dialog->GetScreenPosition() ); SHAPE_POLY_SET dialogPoly( BOX2D( view->ToWorld( dialogPos, true ), @@ -335,6 +334,10 @@ void PCB_BASE_FRAME::FocusOnItem( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer ) if( !clippedPoly.IsEmpty() ) itemPoly = clippedPoly; + /* + * Perform a step-wise deflate to find the visual-center-of-mass + */ + BOX2I bbox = itemPoly.BBox(); int step = std::min( bbox.GetWidth(), bbox.GetHeight() ) / 10;