KiCad PCB EDA Suite
Loading...
Searching...
No Matches
wx_view_controls.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
5 * Copyright (C) 2013-2015 CERN
6 * Copyright (C) 2012-2022 KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * @author Tomasz Wlostowski <[email protected]>
9 * @author Maciej Suminski <[email protected]>
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, you may find one here:
23 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
24 * or you may search the http://www.gnu.org website for the version 2 license,
25 * or you may write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27 */
28
29#include <pgm_base.h>
30#include <core/profile.h>
31#include <view/view.h>
36#include <trace_helpers.h>
38#include <math/util.h> // for KiROUND
40#include <widgets/ui_common.h>
42#include <eda_draw_frame.h>
43#include <kiway.h>
44#include <kiplatform/ui.h>
45#include <wx/log.h>
46
47#ifdef __WXMSW__
48 #define USE_MOUSE_CAPTURE
49#endif
50
51using namespace KIGFX;
52
53const wxEventType WX_VIEW_CONTROLS::EVT_REFRESH_MOUSE = wxNewEventType();
54
55
56static std::unique_ptr<ZOOM_CONTROLLER> GetZoomControllerForPlatform( bool aAcceleration )
57{
58#ifdef __WXMAC__
59 // On Apple pointer devices, wheel events occur frequently and with
60 // smaller rotation values. For those devices, let's handle zoom
61 // based on the rotation amount rather than the time difference.
62 return std::make_unique<CONSTANT_ZOOM_CONTROLLER>( CONSTANT_ZOOM_CONTROLLER::MAC_SCALE );
63#elif __WXGTK3__
64 // GTK3 is similar, but the scale constant is smaller
65 return std::make_unique<CONSTANT_ZOOM_CONTROLLER>( CONSTANT_ZOOM_CONTROLLER::GTK3_SCALE );
66#else
67 if( aAcceleration )
68 return std::make_unique<ACCELERATING_ZOOM_CONTROLLER>();
69 else
70 return std::make_unique<CONSTANT_ZOOM_CONTROLLER>( CONSTANT_ZOOM_CONTROLLER::MSW_SCALE );
71#endif
72}
73
74
76 VIEW_CONTROLS( aView ),
77 m_state( IDLE ),
78 m_parentPanel( aParentPanel ),
79 m_scrollScale( 1.0, 1.0 ),
80#ifdef __WXGTK3__
81 m_lastTimestamp( 0 ),
82#endif
83 m_cursorPos( 0, 0 ),
84 m_updateCursor( true )
85{
87
88 m_MotionEventCounter = std::make_unique<PROF_COUNTER>( "Mouse motion events" );
89
90 m_parentPanel->Connect( wxEVT_MOTION,
91 wxMouseEventHandler( WX_VIEW_CONTROLS::onMotion ), nullptr, this );
92 m_parentPanel->Connect( wxEVT_MAGNIFY,
93 wxMouseEventHandler( WX_VIEW_CONTROLS::onMagnify ), nullptr, this );
94 m_parentPanel->Connect( wxEVT_MOUSEWHEEL,
95 wxMouseEventHandler( WX_VIEW_CONTROLS::onWheel ), nullptr, this );
96 m_parentPanel->Connect( wxEVT_MIDDLE_UP,
97 wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
98 m_parentPanel->Connect( wxEVT_MIDDLE_DOWN,
99 wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
100 m_parentPanel->Connect( wxEVT_LEFT_UP,
101 wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
102 m_parentPanel->Connect( wxEVT_LEFT_DOWN,
103 wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
104 m_parentPanel->Connect( wxEVT_RIGHT_UP,
105 wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
106 m_parentPanel->Connect( wxEVT_RIGHT_DOWN,
107 wxMouseEventHandler( WX_VIEW_CONTROLS::onButton ), nullptr, this );
108#if defined __WXMSW__
109 m_parentPanel->Connect( wxEVT_ENTER_WINDOW,
110 wxMouseEventHandler( WX_VIEW_CONTROLS::onEnter ), nullptr, this );
111#endif
112 m_parentPanel->Connect( wxEVT_LEAVE_WINDOW,
113 wxMouseEventHandler( WX_VIEW_CONTROLS::onLeave ), nullptr, this );
114 m_parentPanel->Connect( wxEVT_SCROLLWIN_THUMBTRACK,
115 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
116 m_parentPanel->Connect( wxEVT_SCROLLWIN_PAGEUP,
117 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
118 m_parentPanel->Connect( wxEVT_SCROLLWIN_PAGEDOWN,
119 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
120
121 m_parentPanel->Connect( wxEVT_SCROLLWIN_BOTTOM,
122 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
123 m_parentPanel->Connect( wxEVT_SCROLLWIN_TOP,
124 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
125 m_parentPanel->Connect( wxEVT_SCROLLWIN_LINEUP,
126 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
127 m_parentPanel->Connect( wxEVT_SCROLLWIN_LINEDOWN,
128 wxScrollWinEventHandler( WX_VIEW_CONTROLS::onScroll ), nullptr, this );
129#if defined USE_MOUSE_CAPTURE
130 m_parentPanel->Connect( wxEVT_MOUSE_CAPTURE_LOST,
131 wxMouseEventHandler( WX_VIEW_CONTROLS::onCaptureLost ), nullptr, this );
132#endif
133
134 m_cursorWarped = false;
135
136 m_panTimer.SetOwner( this );
137 this->Connect( wxEVT_TIMER, wxTimerEventHandler( WX_VIEW_CONTROLS::onTimer ), nullptr, this );
138
142}
143
144
146{
147#if defined USE_MOUSE_CAPTURE
148 if( m_parentPanel->HasCapture() )
149 m_parentPanel->ReleaseMouse();
150#endif
151}
152
153
155{
156 COMMON_SETTINGS* cfg = Pgm().GetCommonSettings();
157
173
174 m_zoomController.reset();
175
176 if( cfg->m_Input.zoom_speed_auto )
177 {
179 }
180 else
181 {
182 if( cfg->m_Input.zoom_acceleration )
183 {
185 std::make_unique<ACCELERATING_ZOOM_CONTROLLER>( cfg->m_Input.zoom_speed );
186 }
187 else
188 {
190
191 m_zoomController = std::make_unique<CONSTANT_ZOOM_CONTROLLER>( scale );
192 }
193 }
194}
195
196
197void WX_VIEW_CONTROLS::onMotion( wxMouseEvent& aEvent )
198{
199 ( *m_MotionEventCounter )++;
200
201 // Because Weston sends a motion event to previous location after warping the pointer
202 wxPoint mouseRel = m_parentPanel->ScreenToClient( KIPLATFORM::UI::GetMousePosition() );
203
204 bool isAutoPanning = false;
205 int x = mouseRel.x;
206 int y = mouseRel.y;
207 VECTOR2D mousePos( x, y );
208
209 // Automatic focus switching between SCH and PCB windows on canvas mouse motion
211 {
213 {
214 KIWAY_PLAYER* otherFrame = nullptr;
215
216 if( frame->IsType( FRAME_PCB_EDITOR ) )
217 {
218 otherFrame = frame->Kiway().Player( FRAME_SCH, false );
219 }
220 else if( frame->IsType( FRAME_SCH ) )
221 {
222 otherFrame = frame->Kiway().Player( FRAME_PCB_EDITOR, false );
223 }
224
225 if( otherFrame && KIPLATFORM::UI::IsWindowActive( otherFrame )
226 && !KIPLATFORM::UI::IsWindowActive( frame ) )
227 {
228 frame->Raise();
229 }
230 }
231 }
232
234 handleCursorCapture( x, y );
235
237 isAutoPanning = handleAutoPanning( aEvent );
238
239 if( !isAutoPanning && aEvent.Dragging() )
240 {
241 if( m_state == DRAG_PANNING )
242 {
243 static bool justWarped = false;
244 int warpX = 0;
245 int warpY = 0;
246 wxSize parentSize = m_parentPanel->GetClientSize();
247
248 if( x < 0 )
249 {
250 warpX = parentSize.x;
251 }
252 else if(x >= parentSize.x )
253 {
254 warpX = -parentSize.x;
255 }
256
257 if( y < 0 )
258 {
259 warpY = parentSize.y;
260 }
261 else if( y >= parentSize.y )
262 {
263 warpY = -parentSize.y;
264 }
265
266 if( !justWarped )
267 {
268 VECTOR2D d = m_dragStartPoint - mousePos;
269 m_dragStartPoint = mousePos;
270 VECTOR2D delta = m_view->ToWorld( d, false );
272 aEvent.StopPropagation();
273 }
274
275 if( warpX || warpY )
276 {
277 if( !justWarped )
278 {
279 if( KIPLATFORM::UI::WarpPointer( m_parentPanel, x + warpX, y + warpY ) )
280 {
281 m_dragStartPoint += VECTOR2D( warpX, warpY );
282 justWarped = true;
283 }
284 }
285 else
286 justWarped = false;
287 }
288 else
289 justWarped = false;
290 }
291 else if( m_state == DRAG_ZOOMING )
292 {
293 static bool justWarped = false;
294 int warpY = 0;
295 wxSize parentSize = m_parentPanel->GetClientSize();
296
297 if( y < 0 )
298 {
299 warpY = parentSize.y;
300 }
301 else if( y >= parentSize.y )
302 {
303 warpY = -parentSize.y;
304 }
305
306 if( !justWarped )
307 {
308 VECTOR2D d = m_dragStartPoint - mousePos;
309 m_dragStartPoint = mousePos;
310
311 double scale = exp( d.y * m_settings.m_zoomSpeed * 0.001 );
312
313 wxLogTrace( traceZoomScroll, wxString::Format( "dy: %f scale: %f", d.y, scale ) );
314
316 aEvent.StopPropagation();
317 }
318
319 if( warpY )
320 {
321 if( !justWarped )
322 {
324 m_dragStartPoint += VECTOR2D( 0, warpY );
325 justWarped = true;
326 }
327 else
328 justWarped = false;
329 }
330 else
331 justWarped = false;
332 }
333 }
334
335 if( m_updateCursor ) // do not update the cursor position if it was explicitly set
336 m_cursorPos = GetClampedCoords( m_view->ToWorld( mousePos ) );
337 else
338 m_updateCursor = true;
339
340 aEvent.Skip();
341}
342
343
344void WX_VIEW_CONTROLS::onWheel( wxMouseEvent& aEvent )
345{
346#ifdef __WXGTK3__
347 if( aEvent.GetTimestamp() == m_lastTimestamp )
348 {
349 aEvent.Skip( false );
350 return;
351 }
352
353 m_lastTimestamp = aEvent.GetTimestamp();
354#endif
355
356 const double wheelPanSpeed = 0.001;
357 const int axis = aEvent.GetWheelAxis();
358
359 if( axis == wxMOUSE_WHEEL_HORIZONTAL && !m_settings.m_horizontalPan )
360 return;
361
362 // Pick the modifier, if any. Shift beats control beats alt, we don't support more than one.
363 int modifiers =
364 aEvent.ShiftDown() ? WXK_SHIFT :
365 ( aEvent.ControlDown() ? WXK_CONTROL : ( aEvent.AltDown() ? WXK_ALT : 0 ) );
366
367 // Restrict zoom handling to the vertical axis, otherwise horizontal
368 // scrolling events (e.g. touchpads and some mice) end up interpreted
369 // as vertical scroll events and confuse the user.
370 if( modifiers == m_settings.m_scrollModifierZoom )
371 {
372 if ( axis == wxMOUSE_WHEEL_VERTICAL )
373 {
374 const int rotation = aEvent.GetWheelRotation();
375 const double zoomScale = m_zoomController->GetScaleForRotation( rotation );
376
378 {
380 m_view->SetScale( m_view->GetScale() * zoomScale );
381 }
382 else
383 {
384 const VECTOR2D anchor = m_view->ToWorld( VECTOR2D( aEvent.GetX(), aEvent.GetY() ) );
385 m_view->SetScale( m_view->GetScale() * zoomScale, anchor );
386 }
387
388 // Refresh the zoom level and mouse position on message panel
389 // (mouse position has not changed, only the zoom level has changed):
390 refreshMouse( true );
391 }
392 }
393 else
394 {
395 // Scrolling
396 VECTOR2D scrollVec = m_view->ToWorld( m_view->GetScreenPixelSize(), false ) *
397 ( (double) aEvent.GetWheelRotation() * wheelPanSpeed );
398 double scrollX = 0.0;
399 double scrollY = 0.0;
400 bool hReverse = false;
401
402 if( axis != wxMOUSE_WHEEL_HORIZONTAL )
404
405 if( axis == wxMOUSE_WHEEL_HORIZONTAL || modifiers == m_settings.m_scrollModifierPanH )
406 {
407 if( hReverse )
408 scrollX = scrollVec.x;
409 else
410 scrollX = ( axis == wxMOUSE_WHEEL_HORIZONTAL ) ? scrollVec.x : -scrollVec.x;
411 }
412 else
413 {
414 scrollY = -scrollVec.y;
415 }
416
417 VECTOR2D delta( scrollX, scrollY );
418
420 refreshMouse( true );
421 }
422
423 // Do not skip this event, otherwise wxWidgets will fire
424 // 3 wxEVT_SCROLLWIN_LINEUP or wxEVT_SCROLLWIN_LINEDOWN (normal wxWidgets behavior)
425 // and we do not want that.
426}
427
428
429void WX_VIEW_CONTROLS::onMagnify( wxMouseEvent& aEvent )
430{
431 // Scale based on the magnification from our underlying magnification event.
432 VECTOR2D anchor = m_view->ToWorld( VECTOR2D( aEvent.GetX(), aEvent.GetY() ) );
433 m_view->SetScale( m_view->GetScale() * ( aEvent.GetMagnification() + 1.0f ), anchor );
434
435 aEvent.Skip();
436}
437
438
440{
441 m_state = aNewState;
442}
443
444void WX_VIEW_CONTROLS::onButton( wxMouseEvent& aEvent )
445{
446 switch( m_state )
447 {
448 case IDLE:
449 case AUTO_PANNING:
450 if( ( aEvent.MiddleDown() && m_settings.m_dragMiddle == MOUSE_DRAG_ACTION::PAN ) ||
451 ( aEvent.RightDown() && m_settings.m_dragRight == MOUSE_DRAG_ACTION::PAN ) )
452 {
453 m_dragStartPoint = VECTOR2D( aEvent.GetX(), aEvent.GetY() );
456
457#if defined USE_MOUSE_CAPTURE
458 if( !m_parentPanel->HasCapture() )
459 m_parentPanel->CaptureMouse();
460#endif
461 }
462 else if( ( aEvent.MiddleDown() && m_settings.m_dragMiddle == MOUSE_DRAG_ACTION::ZOOM ) ||
463 ( aEvent.RightDown() && m_settings.m_dragRight == MOUSE_DRAG_ACTION::ZOOM ) )
464 {
465 m_dragStartPoint = VECTOR2D( aEvent.GetX(), aEvent.GetY() );
468
469#if defined USE_MOUSE_CAPTURE
470 if( !m_parentPanel->HasCapture() )
471 m_parentPanel->CaptureMouse();
472#endif
473 }
474
475 if( aEvent.LeftUp() )
476 setState( IDLE ); // Stop autopanning when user release left mouse button
477
478 break;
479
480 case DRAG_ZOOMING:
481 case DRAG_PANNING:
482 if( aEvent.MiddleUp() || aEvent.LeftUp() || aEvent.RightUp() )
483 {
484 setState( IDLE );
486
487#if defined USE_MOUSE_CAPTURE
488 if( !m_settings.m_cursorCaptured && m_parentPanel->HasCapture() )
489 m_parentPanel->ReleaseMouse();
490#endif
491 }
492
493 break;
494 }
495
496 aEvent.Skip();
497}
498
499
500void WX_VIEW_CONTROLS::onEnter( wxMouseEvent& aEvent )
501{
502 // Avoid stealing focus from text controls
503 // This is particularly important for users using On-Screen-Keyboards
504 // They may move the mouse over the canvas to reach the keyboard
506 {
507 return;
508 }
509
510#if defined( _WIN32 ) || defined( __WXGTK__ )
511 // Win32 and some *nix WMs transmit mouse move and wheel events to all controls below the mouse regardless
512 // of focus. Forcing the focus here will cause the EDA FRAMES to immediately become the
513 // top level active window.
514 if( m_parentPanel->GetParent() != nullptr )
515 {
516 // this assumes the parent panel's parent is the eda window
518 {
520 }
521 }
522#else
524#endif
525}
526
527
528void WX_VIEW_CONTROLS::onLeave( wxMouseEvent& aEvent )
529{
530#if !defined USE_MOUSE_CAPTURE
531 onMotion( aEvent );
532#endif
533}
534
535void WX_VIEW_CONTROLS::onCaptureLost( wxMouseEvent& aEvent )
536{
537 // This method must be present to suppress the capture-lost assertion
538
539 // Set the flag to allow calling m_parentPanel->CaptureMouse()
540 // Note: One cannot call m_parentPanel->CaptureMouse() twice, this is not accepted
541 // by wxWidgets (MSW specific) so we need this guard
543}
544
545
546void WX_VIEW_CONTROLS::onTimer( wxTimerEvent& aEvent )
547{
548 switch( m_state )
549 {
550 case AUTO_PANNING:
551 {
553 {
554 setState( IDLE );
555 return;
556 }
557
558 #ifdef __WXMSW__
559 // Hackfix: It's possible for the mouse to leave the canvas
560 // without triggering any leave events on windows
561 // Use a MSW only wx function
562 if( !m_parentPanel->IsMouseInWindow() )
563 {
564 m_panTimer.Stop();
565 setState( IDLE );
566 return;
567 }
568 #endif
569
570 if( !m_parentPanel->HasFocus() && !m_parentPanel->StatusPopupHasFocus() )
571 {
572 setState( IDLE );
573 return;
574 }
575
576 double borderSize = std::min( m_settings.m_autoPanMargin * m_view->GetScreenPixelSize().x,
578
579 // When the mouse cursor is outside the area with no pan,
580 // m_panDirection is the dist to this area limit ( in pixels )
581 // It will be used also as pan value (the pan speed depends on this dist).
583
584 // When the mouse cursor is outside the area with no pan, the pan value
585 // is accelerated depending on the dist between the area and the cursor
586 float accel = 0.5f + ( m_settings.m_autoPanAcceleration / 5.0f );
587
588 // For a small mouse cursor dist to area, just use the distance.
589 // But for a dist > borderSize / 2, use an accelerated pan value
590
591 if( dir.EuclideanNorm() >= borderSize ) // far from area limits
592 dir = dir.Resize( borderSize * accel );
593 else if( dir.EuclideanNorm() > borderSize / 2 ) // Near from area limits
594 dir = dir.Resize( borderSize );
595
596 dir = m_view->ToWorld( dir, false );
597 m_view->SetCenter( m_view->GetCenter() + dir );
598
599 refreshMouse( true );
600
601 m_panTimer.Start();
602 }
603 break;
604
605 case IDLE: // Just remove unnecessary warnings
606 case DRAG_PANNING:
607 case DRAG_ZOOMING:
608 break;
609 }
610}
611
612
613void WX_VIEW_CONTROLS::onScroll( wxScrollWinEvent& aEvent )
614{
615 const double linePanDelta = 0.05;
616 const double pagePanDelta = 0.5;
617
618 int type = aEvent.GetEventType();
619 int dir = aEvent.GetOrientation();
620
621 if( type == wxEVT_SCROLLWIN_THUMBTRACK )
622 {
623 auto center = m_view->GetCenter();
624 const auto& boundary = m_view->GetBoundary();
625
626 // Flip scroll direction in flipped view
627 const double xstart = ( m_view->IsMirroredX() ?
628 boundary.GetRight() : boundary.GetLeft() );
629 const double xdelta = ( m_view->IsMirroredX() ? -1 : 1 );
630
631 if( dir == wxHORIZONTAL )
632 center.x = xstart + xdelta * ( aEvent.GetPosition() / m_scrollScale.x );
633 else
634 center.y = boundary.GetTop() + aEvent.GetPosition() / m_scrollScale.y;
635
636 m_view->SetCenter( center );
637 }
638 else if( type == wxEVT_SCROLLWIN_THUMBRELEASE ||
639 type == wxEVT_SCROLLWIN_TOP ||
640 type == wxEVT_SCROLLWIN_BOTTOM )
641 {
642 // Do nothing on thumb release, we don't care about it.
643 // We don't have a concept of top or bottom in our viewport, so ignore those events.
644 }
645 else
646 {
647 double dist = 0;
648
649 if( type == wxEVT_SCROLLWIN_PAGEUP )
650 {
651 dist = pagePanDelta;
652 }
653 else if( type == wxEVT_SCROLLWIN_PAGEDOWN )
654 {
655 dist = -pagePanDelta;
656 }
657 else if( type == wxEVT_SCROLLWIN_LINEUP )
658 {
659 dist = linePanDelta;
660 }
661 else if( type == wxEVT_SCROLLWIN_LINEDOWN )
662 {
663 dist = -linePanDelta;
664 }
665 else
666 {
667 wxCHECK_MSG( false, /* void */, wxT( "Unhandled event type" ) );
668 }
669
670 VECTOR2D scroll = m_view->ToWorld( m_view->GetScreenPixelSize(), false ) * dist;
671
672 double scrollX = 0.0;
673 double scrollY = 0.0;
674
675 if ( dir == wxHORIZONTAL )
676 scrollX = -scroll.x;
677 else
678 scrollY = -scroll.y;
679
680 VECTOR2D delta( scrollX, scrollY );
681
683 }
684
686}
687
688
690{
691#if defined USE_MOUSE_CAPTURE
692 // Note: for some reason, m_parentPanel->HasCapture() can be false even if CaptureMouse()
693 // was called (i.e. mouse was captured, so when need to test m_MouseCapturedLost to be
694 // sure a wxEVT_MOUSE_CAPTURE_LOST event was fired before. Otherwise wxMSW complains
695 if( aEnabled && !m_parentPanel->HasCapture() && m_parentPanel->m_MouseCapturedLost )
696 {
697 m_parentPanel->CaptureMouse();
698
699 // Clear the flag to allow calling m_parentPanel->CaptureMouse()
700 // Calling it without calling ReleaseMouse() is not accepted by wxWidgets (MSW specific)
702 }
703 else if( !aEnabled && m_parentPanel->HasCapture()
705 {
706 m_parentPanel->ReleaseMouse();
707
708 // Mouse is released, calling CaptureMouse() is allowed now:
710 }
711#endif
713}
714
715
717{
719 {
720 setState( IDLE );
721#if defined USE_MOUSE_CAPTURE
722 if( !m_settings.m_cursorCaptured && m_parentPanel->HasCapture() )
723 m_parentPanel->ReleaseMouse();
724#endif
725 }
726}
727
728
729VECTOR2D WX_VIEW_CONTROLS::GetMousePosition( bool aWorldCoordinates ) const
730{
731 wxPoint msp = getMouseScreenPosition();
732 VECTOR2D screenPos( msp.x, msp.y );
733
734 return aWorldCoordinates ? GetClampedCoords( m_view->ToWorld( screenPos ) ) : screenPos;
735}
736
737
739{
740 GAL* gal = m_view->GetGAL();
741
742 if( aEnableSnapping && gal->GetGridSnapping() )
743 {
744 return gal->GetGridPoint( m_cursorPos );
745 }
746 else
747 {
748 return m_cursorPos;
749 }
750}
751
752
754{
756 {
758 }
759 else
760 {
761 return GetClampedCoords( GetRawCursorPosition( aEnableSnapping ) );
762 }
763}
764
765
766void WX_VIEW_CONTROLS::SetCursorPosition( const VECTOR2D& aPosition, bool aWarpView,
767 bool aTriggeredByArrows, long aArrowCommand )
768{
769 m_updateCursor = false;
770
771 VECTOR2D clampedPosition = GetClampedCoords( aPosition );
772
773 if( aTriggeredByArrows )
774 {
778 m_cursorWarped = false;
779 }
780 else
781 {
785 m_cursorWarped = true;
786 }
787
788 WarpMouseCursor( clampedPosition, true, aWarpView );
789 m_cursorPos = clampedPosition;
790}
791
792
794 bool aWarpView = true )
795{
796 m_updateCursor = false;
797
798 VECTOR2D clampedPosition = GetClampedCoords( aPosition );
799
800 const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
801 BOX2I screen( VECTOR2I( 0, 0 ), screenSize );
802 VECTOR2D screenPos = m_view->ToScreen( clampedPosition );
803
804 if( aWarpView && !screen.Contains( screenPos ) )
805 m_view->SetCenter( clampedPosition );
806
807 m_cursorPos = clampedPosition;
808}
809
810
811void WX_VIEW_CONTROLS::WarpMouseCursor( const VECTOR2D& aPosition, bool aWorldCoordinates,
812 bool aWarpView )
813{
814 if( aWorldCoordinates )
815 {
816 const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
817 BOX2I screen( VECTOR2I( 0, 0 ), screenSize );
818 VECTOR2D clampedPosition = GetClampedCoords( aPosition );
819 VECTOR2D screenPos = m_view->ToScreen( clampedPosition );
820
821 if( !screen.Contains( screenPos ) )
822 {
823 if( aWarpView )
824 {
825 m_view->SetCenter( clampedPosition );
826 KIPLATFORM::UI::WarpPointer( m_parentPanel, screenSize.x / 2, screenSize.y / 2 );
827 }
828 }
829 else
830 {
831 KIPLATFORM::UI::WarpPointer( m_parentPanel, screenPos.x, screenPos.y );
832 }
833 }
834 else
835 {
836 KIPLATFORM::UI::WarpPointer( m_parentPanel, aPosition.x, aPosition.y );
837 }
838
839 // If we are not refreshing because of mouse movement, don't set the modifiers
840 // because we are refreshing for keyboard movement, which uses the same modifiers for other actions
842}
843
844
846{
847 const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
848 VECTOR2I screenCenter( screenSize / 2 );
849
850 if( GetMousePosition( false ) != screenCenter )
851 {
852 VECTOR2D newCenter = GetCursorPosition();
853
854 if( KIPLATFORM::UI::WarpPointer( m_parentPanel, screenCenter.x, screenCenter.y ) )
855 {
856 m_view->SetCenter( newCenter );
857 m_dragStartPoint = screenCenter;
858 }
859 }
860}
861
862
864{
865 int border = std::min( m_settings.m_autoPanMargin * m_view->GetScreenPixelSize().x,
867 border += 2;
868
869 VECTOR2D topLeft( border, border );
870 VECTOR2D botRight( m_view->GetScreenPixelSize().x - border,
871 m_view->GetScreenPixelSize().y - border );
872
873 topLeft = m_view->ToWorld( topLeft );
874 botRight = m_view->ToWorld( botRight );
875
876 VECTOR2D pos = GetMousePosition( true );
877
878 if( pos.x < topLeft.x )
879 pos.x = topLeft.x;
880 else if( pos.x > botRight.x )
881 pos.x = botRight.x;
882
883 if( pos.y < topLeft.y )
884 pos.y = topLeft.y;
885 else if( pos.y > botRight.y )
886 pos.y = botRight.y;
887
888 SetCursorPosition( pos, false, false, 0 );
889
890 if( aWarpMouseCursor )
891 WarpMouseCursor( pos, true );
892}
893
894
895bool WX_VIEW_CONTROLS::handleAutoPanning( const wxMouseEvent& aEvent )
896{
897 VECTOR2I p( aEvent.GetX(), aEvent.GetY() );
899
901 {
902 // last cursor move event came from keyboard cursor control. If auto-panning is enabled
903 // and the next position is inside the autopan zone, check if it really came from a mouse
904 // event, otherwise disable autopan temporarily. Also temporarily disable autopan if the
905 // cursor is in the autopan zone because the application warped the cursor.
906
907 m_cursorWarped = false;
908 return true;
909 }
910
911 m_cursorWarped = false;
912
913 // Compute areas where autopanning is active
914 int borderStart = std::min( m_settings.m_autoPanMargin * m_view->GetScreenPixelSize().x,
916 borderStart = std::max( borderStart, 2 );
917 int borderEndX = m_view->GetScreenPixelSize().x - borderStart;
918 int borderEndY = m_view->GetScreenPixelSize().y - borderStart;
919
920 if( p.x < borderStart )
921 m_panDirection.x = -( borderStart - p.x );
922 else if( p.x > borderEndX )
923 m_panDirection.x = ( p.x - borderEndX );
924 else
925 m_panDirection.x = 0;
926
927 if( p.y < borderStart )
928 m_panDirection.y = -( borderStart - p.y );
929 else if( p.y > borderEndY )
930 m_panDirection.y = ( p.y - borderEndY );
931 else
932 m_panDirection.y = 0;
933
934 bool borderHit = ( m_panDirection.x != 0 || m_panDirection.y != 0 );
935
936 switch( m_state )
937 {
938 case AUTO_PANNING:
939 if( !borderHit )
940 {
941 m_panTimer.Stop();
942 setState( IDLE );
943
944 return false;
945 }
946
947 return true;
948
949 case IDLE:
950 if( borderHit )
951 {
953 m_panTimer.Start( (int) ( 250.0 / 60.0 ), true );
954
955 return true;
956 }
957
958 return false;
959
960 case DRAG_PANNING:
961 case DRAG_ZOOMING:
962 return false;
963 }
964
965 wxCHECK_MSG( false, false, wxT( "This line should never be reached" ) );
966
967 return false;
968}
969
970
972{
974 {
975 bool warp = false;
976 wxSize parentSize = m_parentPanel->GetClientSize();
977
978 if( x < 0 )
979 {
980 x = 0;
981 warp = true;
982 }
983 else if( x >= parentSize.x )
984 {
985 x = parentSize.x - 1;
986 warp = true;
987 }
988
989 if( y < 0 )
990 {
991 y = 0;
992 warp = true;
993 }
994 else if( y >= parentSize.y )
995 {
996 y = parentSize.y - 1;
997 warp = true;
998 }
999
1000 if( warp )
1002 }
1003}
1004
1005
1006void WX_VIEW_CONTROLS::refreshMouse( bool aSetModifiers )
1007{
1008 // Notify tools that the cursor position has changed in the world coordinates
1009 wxMouseEvent moveEvent( EVT_REFRESH_MOUSE );
1010 wxPoint msp = getMouseScreenPosition();
1011 moveEvent.SetX( msp.x );
1012 moveEvent.SetY( msp.y );
1013
1014 if( aSetModifiers )
1015 {
1016 // Set the modifiers state
1017 moveEvent.SetControlDown( wxGetKeyState( WXK_CONTROL ) );
1018 moveEvent.SetShiftDown( wxGetKeyState( WXK_SHIFT ) );
1019 moveEvent.SetAltDown( wxGetKeyState( WXK_ALT ) );
1020 }
1021
1022 m_cursorPos = GetClampedCoords( m_view->ToWorld( VECTOR2D( msp.x, msp.y ) ) );
1023 wxPostEvent( m_parentPanel, moveEvent );
1024}
1025
1026
1028{
1029 wxPoint msp = KIPLATFORM::UI::GetMousePosition();
1030 m_parentPanel->ScreenToClient( &msp.x, &msp.y );
1031 return msp;
1032}
1033
1034
1036{
1037 const BOX2D viewport = m_view->GetViewport();
1038 const BOX2D& boundary = m_view->GetBoundary();
1039
1040 m_scrollScale.x = 2e3 / viewport.GetWidth(); // TODO it does not have to be updated so often
1041 m_scrollScale.y = 2e3 / viewport.GetHeight();
1042 VECTOR2I newScroll( ( viewport.Centre().x - boundary.GetLeft() ) * m_scrollScale.x,
1043 ( viewport.Centre().y - boundary.GetTop() ) * m_scrollScale.y );
1044
1045 // We add the width of the scroll bar thumb to the range because the scroll range is given by
1046 // the full bar while the position is given by the left/top position of the thumb
1047 VECTOR2I newRange( m_scrollScale.x * boundary.GetWidth() +
1048 m_parentPanel->GetScrollThumb( wxSB_HORIZONTAL ),
1049 m_scrollScale.y * boundary.GetHeight() +
1050 m_parentPanel->GetScrollThumb( wxSB_VERTICAL ) );
1051
1052 // Flip scroll direction in flipped view
1053 if( m_view->IsMirroredX() )
1054 newScroll.x = ( boundary.GetRight() - viewport.Centre().x ) * m_scrollScale.x;
1055
1056 // Adjust scrollbars only if it is needed. Otherwise there are cases when canvas is continuously
1057 // refreshed (Windows)
1058 if( m_scrollPos != newScroll || newRange.x != m_parentPanel->GetScrollRange( wxSB_HORIZONTAL )
1059 || newRange.y != m_parentPanel->GetScrollRange( wxSB_VERTICAL ) )
1060 {
1061 m_parentPanel->SetScrollbars( 1, 1, newRange.x, newRange.y, newScroll.x, newScroll.y,
1062 true );
1063 m_scrollPos = newScroll;
1064
1065#if !defined( __APPLE__ ) && !defined( WIN32 )
1066 // Trigger a mouse refresh to get the canvas update in GTK (re-draws the scrollbars).
1067 // Note that this causes an infinite loop on OSX and Windows (in certain cases) as it
1068 // generates a paint event.
1069 refreshMouse( false );
1070#endif
1071 }
1072}
1073
1074void WX_VIEW_CONTROLS::ForceCursorPosition( bool aEnabled, const VECTOR2D& aPosition )
1075{
1076 VECTOR2D clampedPosition = GetClampedCoords( aPosition );
1077
1079 m_settings.m_forcedPosition = clampedPosition;
1080}
coord_type GetTop() const
Definition: box2.h:195
coord_type GetHeight() const
Definition: box2.h:189
coord_type GetWidth() const
Definition: box2.h:188
bool Contains(const Vec &aPoint) const
Definition: box2.h:142
Vec Centre() const
Definition: box2.h:71
coord_type GetRight() const
Definition: box2.h:190
coord_type GetLeft() const
Definition: box2.h:194
The base class for create windows for drawing purpose.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
bool m_MouseCapturedLost
used on wxMSW: true after a wxEVT_MOUSE_CAPTURE_LOST was received false after the mouse is recaptured...
void SetFocus() override
EDA_DRAW_FRAME * GetParentEDAFrame() const
Returns parent EDA_DRAW_FRAME, if available or NULL otherwise.
static constexpr double MAC_SCALE
static constexpr double MSW_SCALE
A suitable (magic) scale factor for Windows systems.
static constexpr double GTK3_SCALE
A suitable (magic) scale factor for Mac systems.
static constexpr double MANUAL_SCALE_FACTOR
Abstract interface for drawing on a 2D-surface.
VECTOR2D GetGridPoint(const VECTOR2D &aPoint) const
For a given point it returns the nearest point belonging to the grid in world coordinates.
bool GetGridSnapping() const
const VECTOR2I & GetScreenPixelSize() const
Return GAL canvas size in pixels.
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
bool m_cursorWarped
Current VIEW_CONTROLS settings.
bool IsCursorWarpingEnabled() const
VC_SETTINGS m_settings
VIEW * m_view
< Pointer to controlled VIEW.
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:68
double GetScale() const
Definition: view.h:271
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition: view.cpp:507
const VECTOR2D & GetCenter() const
Return the center point of this VIEW (in world space coordinates).
Definition: view.h:341
virtual void SetScale(double aScale, VECTOR2D aAnchor={ 0, 0 })
Set the scaling factor, zooming around a given anchor point.
Definition: view.cpp:547
VECTOR2D ToScreen(const VECTOR2D &aCoord, bool aAbsolute=true) const
Convert a world space point/vector to a point/vector in screen space coordinates.
Definition: view.cpp:463
const VECTOR2I & GetScreenPixelSize() const
Return the size of the our rendering area in pixels.
Definition: view.cpp:1183
GAL * GetGAL() const
Return the #GAL this view is using to draw graphical primitives.
Definition: view.h:197
VECTOR2D ToWorld(const VECTOR2D &aCoord, bool aAbsolute=true) const
Converts a screen space point/vector to a point/vector in world space coordinates.
Definition: view.cpp:444
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition: view.h:245
const BOX2D & GetBoundary() const
Definition: view.h:300
void SetCenter(const VECTOR2D &aCenter)
Set the center point of the VIEW (i.e.
Definition: view.cpp:573
bool m_updateCursor
A #ZOOM_CONTROLLER that determines zoom steps. This is platform-specific.
void SetCrossHairCursorPosition(const VECTOR2D &aPosition, bool aWarpView) override
void LoadSettings() override
Event that forces mouse move event in the dispatcher (eg.
void setState(STATE aNewState)
Sets the interaction state, simply a internal setter to make it easier to debug changes.
STATE m_state
Panel that is affected by VIEW_CONTROLS.
void CaptureCursor(bool aEnabled) override
Force the cursor to stay within the drawing panel area.
void onScroll(wxScrollWinEvent &aEvent)
wxTimer m_panTimer
Ratio used for scaling world coordinates to scrollbar position.
void onButton(wxMouseEvent &aEvent)
void onEnter(wxMouseEvent &WXUNUSED(aEvent))
void onWheel(wxMouseEvent &aEvent)
Handler functions.
VECTOR2I m_scrollPos
The mouse position when a drag zoom started.
void refreshMouse(bool aSetModifiers)
Send an event to refresh mouse position.
WX_VIEW_CONTROLS(VIEW *aView, EDA_DRAW_PANEL_GAL *aParentPanel)
void PinCursorInsideNonAutoscrollArea(bool aWarpMouseCursor) override
Return the current mouse pointer position.
void SetCursorPosition(const VECTOR2D &aPosition, bool warpView, bool aTriggeredByArrows, long aArrowCommand) override
Move the graphic crosshair cursor to the requested position expressed in world coordinates.
VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const override
Return the current mouse pointer position.
void WarpMouseCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false) override
Set the viewport center to the current cursor position and warps the cursor to the screen center.
void onMagnify(wxMouseEvent &aEvent)
VECTOR2D GetRawCursorPosition(bool aSnappingEnabled=true) const override
Return the current cursor position in world coordinates ignoring the cursorUp position force mode.
VECTOR2D m_scrollScale
Current scrollbar position.
bool handleAutoPanning(const wxMouseEvent &aEvent)
Compute new viewport settings while in autopanning mode.
wxPoint getMouseScreenPosition() const
Get the cursor position in the screen coordinates.
void onTimer(wxTimerEvent &WXUNUSED(aEvent))
std::unique_ptr< ZOOM_CONTROLLER > m_zoomController
STATE
< Possible states for WX_VIEW_CONTROLS.
@ DRAG_PANNING
Panning with mouse button pressed.
@ AUTO_PANNING
Panning on approaching borders of the frame.
@ DRAG_ZOOMING
Zooming with mouse button pressed.
@ IDLE
Nothing is happening.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
void CenterOnCursor() override
Adjusts the scrollbars position to match the current viewport.
VECTOR2D m_cursorPos
Flag deciding whether the cursor position should be calculated using the mouse position.
void onCaptureLost(wxMouseEvent &WXUNUSED(aEvent))
void onMotion(wxMouseEvent &aEvent)
std::unique_ptr< PROF_COUNTER > m_MotionEventCounter
EDA_DRAW_PANEL_GAL * m_parentPanel
Store information about point where dragging has started.
void UpdateScrollbars()
End any mouse drag action still in progress.
void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0)) override
Applies VIEW_CONTROLS settings from the program COMMON_SETTINGS.
void handleCursorCapture(int x, int y)
Limit the cursor position to within the canvas by warping it.
void onLeave(wxMouseEvent &WXUNUSED(aEvent))
VECTOR2D m_zoomStartPoint
Current cursor position (world coordinates).
static const wxEventType EVT_REFRESH_MOUSE
VECTOR2D m_panDirection
Timer responsible for handling autopanning.
VECTOR2D m_dragStartPoint
Current direction of panning (only autopanning mode).
KIWAY & Kiway() const
Return a reference to the KIWAY that this object has an opportunity to participate in.
Definition: kiway_holder.h:53
A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of a KiCad project.
Definition: kiway_player.h:67
virtual KIWAY_PLAYER * Player(FRAME_T aFrameType, bool doCreate=true, wxTopLevelWindow *aParent=nullptr)
Return the KIWAY_PLAYER* given a FRAME_T.
Definition: kiway.cpp:432
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:265
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:350
@ FRAME_PCB_EDITOR
Definition: frame_type.h:42
@ FRAME_SCH
Definition: frame_type.h:34
a few functions useful in geometry calculations.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamps a vector to values that can be negated, respecting numeric limits of coordinates data type wit...
const wxChar *const traceZoomScroll
Flag to enable debug output of zoom-scrolling calculations in KIGFX::ZOOM_CONTROLLER and derivatives.
The Cairo implementation of the graphics abstraction layer.
Definition: color4d.cpp:247
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition: gtk/ui.cpp:588
void InfiniteDragReleaseWindow()
On Wayland, allows the cursor to freely move again after a drag (see InfiniteDragPrepareWindow).
Definition: gtk/ui.cpp:582
void InfiniteDragPrepareWindow(wxWindow *aWindow)
On Wayland, restricts the pointer movement to a rectangle slightly bigger than the given wxWindow.
Definition: gtk/ui.cpp:576
bool WarpPointer(wxWindow *aWindow, int aX, int aY)
Move the mouse cursor to a specific position relative to the window.
Definition: gtk/ui.cpp:253
bool IsWindowActive(wxWindow *aWindow)
Check to see if the given window is the currently active window (e.g.
Definition: gtk/ui.cpp:73
bool IsInputControlFocused(wxWindow *aFocus=nullptr)
Check if a input control has focus.
Definition: ui_common.cpp:260
see class PGM_BASE
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:119
const int scale
MOUSE_DRAG_ACTION drag_right
MOUSE_DRAG_ACTION drag_middle
MOUSE_DRAG_ACTION drag_left
float m_autoPanMargin
How fast is panning when in auto mode.
Definition: view_controls.h:80
VECTOR2D m_forcedPosition
Is the forced cursor position enabled.
Definition: view_controls.h:56
MOUSE_DRAG_ACTION m_dragLeft
bool m_horizontalPan
Enable the accelerating zoom controller.
Definition: view_controls.h:92
bool m_autoPanSettingEnabled
Distance from cursor to VIEW edge when panning is active.
Definition: view_controls.h:77
bool m_focusFollowSchPcb
Flag for turning on autopanning.
Definition: view_controls.h:71
float m_autoPanAcceleration
If the cursor is allowed to be warped.
Definition: view_controls.h:86
MOUSE_DRAG_ACTION m_dragMiddle
bool m_cursorCaptured
Should the cursor snap to grid or move freely.
Definition: view_controls.h:62
int m_zoomSpeed
When true, ignore zoom_speed and pick a platform-specific default.
Definition: view_controls.h:98
int m_scrollModifierZoom
What modifier key to enable horizontal pan with the (vertical) scroll wheel.
int m_scrollModifierPanH
What modifier key to enable vertical with the (vertical) scroll wheel.
VECTOR2D m_lastKeyboardCursorPosition
Wether to invert the scroll wheel movement for horizontal pan.
bool m_warpCursor
Enable horizontal panning with the horizontal scroll/trackpad input.
Definition: view_controls.h:89
MOUSE_DRAG_ACTION m_dragRight
Is last cursor motion event coming from keyboard arrow cursor motion action.
bool m_zoomAcceleration
Zoom speed for the non-accelerating zoom controller.
Definition: view_controls.h:95
bool m_lastKeyboardCursorPositionValid
ACTIONS::CURSOR_UP, ACTIONS::CURSOR_DOWN, etc.
bool m_zoomSpeedAuto
What modifier key to enable zoom with the (vertical) scroll wheel.
bool m_autoPanEnabled
Flag for turning on autopanning.
Definition: view_controls.h:74
long m_lastKeyboardCursorCommand
Position of the above event.
bool m_forceCursorPosition
Should the cursor be locked within the parent window area.
Definition: view_controls.h:59
constexpr int delta
wxLogTrace helper definitions.
Functions to provide common constants and other functions to assist in making a consistent UI.
VECTOR2< double > VECTOR2D
Definition: vector2d.h:587
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588
static std::unique_ptr< ZOOM_CONTROLLER > GetZoomControllerForPlatform(bool aAcceleration)
WX_VIEW_CONTROLS class definition.
ZOOM_CONTROLLER class definition.