KiCad PCB EDA Suite
Loading...
Searching...
No Matches
action_toolbar.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) 2019 CERN
5 * Copyright The KiCad Developers, see CHANGELOG.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <algorithm>
26#include <advanced_config.h>
27#include <bitmaps.h>
28#include <bitmap_store.h>
29#include <eda_draw_frame.h>
30#include <functional>
31#include <kiplatform/ui.h>
32#include <math/util.h>
33#include <memory>
34#include <pgm_base.h>
36#include <trace_helpers.h>
37#include <tool/action_toolbar.h>
38#include <tool/actions.h>
39#include <tool/tool_action.h>
40#include <tool/tool_event.h>
42#include <tool/tool_manager.h>
46
47#include <wx/log.h>
48#include <wx/popupwin.h>
49#include <wx/renderer.h>
50#include <wx/sizer.h>
51#include <wx/dcclient.h>
52#include <wx/settings.h>
53
54// Needed to handle adding the plugins to the toolbar
55// TODO (ISM): This should be better abstracted away from the toolbars
56#include <python_scripting.h>
58
59
60ACTION_GROUP::ACTION_GROUP( const std::string_view& aName )
61{
62 m_name = aName;
64 m_defaultAction = nullptr;
65}
66
67
68ACTION_GROUP::ACTION_GROUP( const std::string_view& aName,
69 const std::vector<const TOOL_ACTION*>& aActions )
70{
71 m_name = aName;
73
74 SetActions( aActions );
75}
76
77
78void ACTION_GROUP::SetActions( const std::vector<const TOOL_ACTION*>& aActions )
79{
80 wxASSERT_MSG( aActions.size() > 0, wxS( "Action groups must have at least one action" ) );
81
82 // The default action is just the first action in the vector
83 m_actions = aActions;
85}
86
88{
90}
91
92
94{
95 bool valid = std::any_of( m_actions.begin(), m_actions.end(),
96 [&]( const TOOL_ACTION* aAction ) -> bool
97 {
98 // For some reason, we can't compare the actions directly
99 return aAction->GetId() == aDefault.GetId();
100 } );
101
102 wxASSERT_MSG( valid, wxS( "Action must be present in a group to be the default" ) );
103
104 m_defaultAction = &aDefault;
105}
106
107
108#define PALETTE_BORDER 4 // The border around the palette buttons on all sides
109#define BUTTON_BORDER 1 // The border on the sides of the buttons that touch other buttons
110
111
112ACTION_TOOLBAR_PALETTE::ACTION_TOOLBAR_PALETTE( wxWindow* aParent, bool aVertical ) :
113 wxPopupTransientWindow( aParent, wxBORDER_NONE ),
114 m_group( nullptr ),
115 m_isVertical( aVertical ),
116 m_panel( nullptr ),
117 m_mainSizer( nullptr ),
118 m_buttonSizer( nullptr )
119{
120 m_panel = new wxPanel( this, wxID_ANY );
121 m_panel->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
122
123 // This sizer holds the buttons for the actions
124 m_buttonSizer = new wxBoxSizer( aVertical ? wxVERTICAL : wxHORIZONTAL );
125
126 // This sizer holds the other sizer, so that a consistent border is present on all sides
127 m_mainSizer = new wxBoxSizer( aVertical ? wxVERTICAL : wxHORIZONTAL );
128 m_mainSizer->Add( m_buttonSizer, wxSizerFlags().Border( wxALL, PALETTE_BORDER ) );
129
130 m_panel->SetSizer( m_mainSizer );
131
132 Connect( wxEVT_CHAR_HOOK, wxCharEventHandler( ACTION_TOOLBAR_PALETTE::onCharHook ),
133 nullptr, this );
134}
135
136
138{
140 wxBitmapBundle normalBmp = KiBitmapBundle( aAction.GetIcon(), size );
141
142 int bmpWidth = normalBmp.GetPreferredBitmapSizeFor( this ).GetWidth();
143 int padding = ( m_buttonSize.GetWidth() - bmpWidth ) / 2;
144 wxSize bmSize( size, size );
145 bmSize *= KIPLATFORM::UI::GetContentScaleFactor( m_parent );
146
147 BITMAP_BUTTON* button = new BITMAP_BUTTON( m_panel, aAction.GetUIId() );
148
149 button->SetIsToolbarButton();
150 button->SetBitmap( normalBmp );
151 button->SetDisabledBitmap( KiDisabledBitmapBundle( aAction.GetIcon() ) );
152 button->SetPadding( padding );
153 button->SetToolTip( aAction.GetButtonTooltip() );
154 button->AcceptDragInAsClick();
155 button->SetBitmapCentered();
156
157 m_buttons[aAction.GetUIId()] = button;
158
159 if( m_isVertical )
160 m_buttonSizer->Add( button, wxSizerFlags().Border( wxTOP | wxBOTTOM, BUTTON_BORDER ) );
161 else
162 m_buttonSizer->Add( button, wxSizerFlags().Border( wxLEFT | wxRIGHT, BUTTON_BORDER ) );
163
164 m_buttonSizer->Layout();
165}
166
167
168void ACTION_TOOLBAR_PALETTE::EnableAction( const TOOL_ACTION& aAction, bool aEnable )
169{
170 auto it = m_buttons.find( aAction.GetUIId() );
171
172 if( it != m_buttons.end() )
173 it->second->Enable( aEnable );
174}
175
176
177void ACTION_TOOLBAR_PALETTE::CheckAction( const TOOL_ACTION& aAction, bool aCheck )
178{
179 auto it = m_buttons.find( aAction.GetUIId() );
180
181 if( it != m_buttons.end() )
182 it->second->Check( aCheck );
183}
184
185
186void ACTION_TOOLBAR_PALETTE::Popup( wxWindow* aFocus )
187{
188 m_mainSizer->Fit( m_panel );
189 SetClientSize( m_panel->GetSize() );
190
191 wxPopupTransientWindow::Popup( aFocus );
192}
193
194
195void ACTION_TOOLBAR_PALETTE::onCharHook( wxKeyEvent& aEvent )
196{
197 // Allow the escape key to dismiss this popup
198 if( aEvent.GetKeyCode() == WXK_ESCAPE )
199 Dismiss();
200 else
201 aEvent.Skip();
202}
203
204
205ACTION_TOOLBAR::ACTION_TOOLBAR( EDA_BASE_FRAME* parent, wxWindowID id, const wxPoint& pos, const wxSize& size,
206 long style ) :
207 wxAuiToolBar( parent, id, pos, size, style ),
208 m_paletteTimer( nullptr ),
209 m_auiManager( nullptr ),
210 m_toolManager( parent->GetToolManager() ),
211 m_palette( nullptr )
212{
213 m_paletteTimer = new wxTimer( this );
214
215 SetArtProvider( new WX_AUI_TOOLBAR_ART );
216
217 Connect( wxEVT_COMMAND_TOOL_CLICKED, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolEvent ), nullptr, this );
218 Connect( wxEVT_AUITOOLBAR_RIGHT_CLICK, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onRightClick ), nullptr, this );
219 Connect( wxEVT_AUITOOLBAR_BEGIN_DRAG, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onItemDrag ), nullptr, this );
220 Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
221 Connect( wxEVT_LEFT_UP, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
222 Connect( m_paletteTimer->GetId(), wxEVT_TIMER, wxTimerEventHandler( ACTION_TOOLBAR::onTimerDone ), nullptr, this );
223
224 Bind( wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEventHandler( ACTION_TOOLBAR::onThemeChanged ), this );
225}
226
227
229{
230 Disconnect( wxEVT_COMMAND_TOOL_CLICKED, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolEvent ), nullptr, this );
231 Disconnect( wxEVT_AUITOOLBAR_RIGHT_CLICK, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onRightClick ), nullptr, this );
232 Disconnect( wxEVT_AUITOOLBAR_BEGIN_DRAG, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onItemDrag ), nullptr, this );
233 Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
234 Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
235 Disconnect( m_paletteTimer->GetId(), wxEVT_TIMER, wxTimerEventHandler( ACTION_TOOLBAR::onTimerDone ), nullptr,
236 this );
237
238 Unbind( wxEVT_SYS_COLOUR_CHANGED, wxSysColourChangedEventHandler( ACTION_TOOLBAR::onThemeChanged ), this );
239
240 delete m_paletteTimer;
241
242 // Clear all the maps keeping track of our items on the toolbar
243 m_toolMenus.clear();
244 m_actionGroups.clear();
245 m_toolCancellable.clear();
246 m_toolKinds.clear();
247 m_toolActions.clear();
248}
249
250
252{
253 wxASSERT( GetParent() );
254
255 std::map<std::string, std::string> currentGroupItems;
256
257 for( const auto& [id, group] : m_actionGroups )
258 {
259 if( m_toolActions[group->GetUIId()] )
260 currentGroupItems[group->GetName()] = m_toolActions[group->GetUIId()]->GetName();
261 }
262
263 // Remove existing tools
264 ClearToolbar();
265
266 std::vector<TOOLBAR_ITEM> items = aConfig.GetToolbarItems();
267
268 // Add all the items to the toolbar
269 for( auto& item : items )
270 {
271 switch( item.m_Type )
272 {
274 AddScaledSeparator( GetParent() );
275 break;
276
278 AddSpacer( item.m_Size );
279 break;
280
282 {
283 // Add a group of items to the toolbar
284 std::string groupName = item.m_GroupName.ToStdString();
285 std::vector<const TOOL_ACTION*> tools;
286 const TOOL_ACTION* defaultTool = nullptr;
287
288 for( TOOLBAR_ITEM& groupItem : item.m_GroupItems )
289 {
290 switch( groupItem.m_Type )
291 {
296 wxFAIL_MSG( wxT( "Unsupported group item type" ) );
297 continue;
298
300 TOOL_ACTION* grpAction = m_toolManager->GetActionManager()->FindAction( groupItem.m_ActionName );
301
302 if( !grpAction )
303 {
304 wxFAIL_MSG( wxString::Format( wxT( "Unable to find group tool %s" ), groupItem.m_ActionName ) );
305 continue;
306 }
307
308 tools.push_back( grpAction );
309
310 if( currentGroupItems[groupName] == groupItem.m_ActionName )
311 defaultTool = grpAction;
312 }
313 }
314
315 std::unique_ptr<ACTION_GROUP> group = std::make_unique<ACTION_GROUP>( groupName, tools );
316
317 if( defaultTool )
318 group->SetDefaultAction( *defaultTool );
319
320 AddGroup( std::move( group ) );
321 break;
322 }
323
325 {
326 // Add a custom control to the toolbar
327 EDA_BASE_FRAME* frame = static_cast<EDA_BASE_FRAME*>( GetParent() );
328 ACTION_TOOLBAR_CONTROL_FACTORY* factory = frame->GetCustomToolbarControlFactory( item.m_ControlName );
329
330 if( !factory )
331 {
332 wxFAIL_MSG( wxString::Format( wxT( "Unable to find control factory for %s" ), item.m_ControlName ) );
333 continue;
334 }
335
336 // The factory functions are responsible for adding the controls to the toolbar themselves
337 (*factory)( this );
338 break;
339 }
340
342 {
343 TOOL_ACTION* action = m_toolManager->GetActionManager()->FindAction( item.m_ActionName );
344
345 if( !action )
346 {
347 wxFAIL_MSG( wxString::Format( wxT( "Unable to find toolbar tool %s" ), item.m_ActionName ) );
348 continue;
349 }
350
351 Add( *action );
352 break;
353 }
354 }
355 }
356
357 // Apply the configuration
358 KiRealize();
359}
360
361
362void ACTION_TOOLBAR::Add( const TOOL_ACTION& aAction )
363{
364 wxASSERT_MSG( !aAction.CheckToolbarState( TOOLBAR_STATE::HIDDEN ),
365 wxString::Format( "Attempting to add hidden action %s to the toolbar", aAction.GetName() ) );
366
367 bool isToggleEntry = aAction.CheckToolbarState( TOOLBAR_STATE::TOGGLE );
368 bool isCancellable = aAction.CheckToolbarState( TOOLBAR_STATE::CANCEL );
369
370 Add( aAction, isToggleEntry, isCancellable );
371}
372
373
374void ACTION_TOOLBAR::Add( const TOOL_ACTION& aAction, bool aIsToggleEntry, bool aIsCancellable )
375{
376 wxASSERT( GetParent() );
377 wxASSERT_MSG( !( aIsCancellable && !aIsToggleEntry ),
378 wxS( "aIsCancellable requires aIsToggleEntry" ) );
379
380 int toolId = aAction.GetUIId();
381
382 AddTool( toolId, wxEmptyString,
383 KiBitmapBundle( aAction.GetIcon(),
384 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ),
385 KiDisabledBitmapBundle( aAction.GetIcon() ),
386 aIsToggleEntry ? wxITEM_CHECK : wxITEM_NORMAL,
387 aAction.GetButtonTooltip(), wxEmptyString, nullptr );
388
389 m_toolKinds[ toolId ] = aIsToggleEntry;
390 m_toolActions[ toolId ] = &aAction;
391 m_toolCancellable[ toolId ] = aIsCancellable;
392}
393
394
396{
397 int toolId = aAction.GetUIId();
398
399 AddTool( toolId, wxEmptyString,
400 KiBitmapBundle( aAction.GetIcon(),
401 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ),
402 KiDisabledBitmapBundle( aAction.GetIcon() ), wxITEM_NORMAL,
403 aAction.GetButtonTooltip(), wxEmptyString, nullptr );
404
405 m_toolKinds[ toolId ] = false;
406 m_toolActions[ toolId ] = &aAction;
407}
408
409
410void ACTION_TOOLBAR::AddScaledSeparator( wxWindow* aWindow )
411{
412 int scale = KiIconScale( aWindow );
413
414 if( scale > 4 )
415 AddSpacer( 16 * ( scale - 4 ) / 4 );
416
417 AddSeparator();
418
419 if( scale > 4 )
420 AddSpacer( 16 * ( scale - 4 ) / 4 );
421}
422
423
424void ACTION_TOOLBAR::Add( wxControl* aControl, const wxString& aLabel )
425{
426 wxASSERT( aControl );
427 m_controlIDs.push_back( aControl->GetId() );
428 AddControl( aControl, aLabel );
429}
430
431
433 std::unique_ptr<ACTION_MENU> aMenu )
434{
435 int toolId = aAction.GetUIId();
436
437 m_toolMenus[toolId] = std::move( aMenu );
438}
439
440
441void ACTION_TOOLBAR::AddGroup( std::unique_ptr<ACTION_GROUP> aGroup )
442{
443 int groupId = aGroup->GetUIId();
444 const TOOL_ACTION* defaultAction = aGroup->GetDefaultAction();
445
446 wxASSERT( GetParent() );
447 wxASSERT( defaultAction );
448
449 // Turn this into a toggle entry if any one of the actions is a toggle entry
450 bool isToggleEntry = false;
451
452 for( const auto& act : aGroup->GetActions() )
453 isToggleEntry |= act->CheckToolbarState( TOOLBAR_STATE::TOGGLE );
454
455
456 m_toolKinds[ groupId ] = isToggleEntry;
457 m_toolActions[ groupId ] = defaultAction;
458 m_actionGroups[ groupId ] = std::move( aGroup );
459
460 // Add the main toolbar item representing the group
461 AddTool( groupId, wxEmptyString,
462 KiBitmapBundle( defaultAction->GetIcon(),
463 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ),
464 KiDisabledBitmapBundle( defaultAction->GetIcon() ),
465 isToggleEntry ? wxITEM_CHECK : wxITEM_NORMAL,
466 wxEmptyString, wxEmptyString, nullptr );
467
468 // Select the default action
469 doSelectAction( m_actionGroups[ groupId ].get(), *defaultAction );
470}
471
472
474{
475 bool valid = std::any_of( aGroup->m_actions.begin(), aGroup->m_actions.end(),
476 [&]( const TOOL_ACTION* action2 ) -> bool
477 {
478 // For some reason, we can't compare the actions directly
479 return aAction.GetId() == action2->GetId();
480 } );
481
482 if( valid )
483 doSelectAction( aGroup, aAction );
484}
485
486
488{
489 // Find the group that contains this action and select it
490 for( auto& [id, groupPtr] : m_actionGroups )
491 {
492 ACTION_GROUP* group = groupPtr.get();
493
494 bool inGroup = std::any_of( group->m_actions.begin(), group->m_actions.end(),
495 [&]( const TOOL_ACTION* action2 )
496 {
497 return aAction.GetId() == action2->GetId();
498 } );
499
500 if( inGroup )
501 {
502 doSelectAction( group, aAction );
503 break;
504 }
505 }
506}
507
508
510{
511 wxASSERT( GetParent() );
512
513 int groupId = aGroup->GetUIId();
514
515 wxAuiToolBarItem* item = FindTool( groupId );
516
517 if( !item )
518 return;
519
520 // Update the item information
521 item->SetShortHelp( aAction.GetButtonTooltip() );
522 item->SetBitmap( KiBitmapBundle( aAction.GetIcon(),
523 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ) );
524 item->SetDisabledBitmap( KiDisabledBitmapBundle( aAction.GetIcon() ) );
525
526 // Register a new handler with the new UI conditions
527 if( m_toolManager )
528 {
529 m_toolManager->GetToolHolder()->UnregisterUIUpdateHandler( groupId );
530
531 const ACTION_CONDITIONS* cond = m_toolManager->GetActionManager()->GetCondition( aAction );
532
533 // Register the new UI condition to control this entry
534 if( cond )
535 {
536 m_toolManager->GetToolHolder()->RegisterUIUpdateHandler( groupId, *cond );
537 }
538 else
539 {
540 wxLogTrace( kicadTraceToolStack, wxString::Format( "No UI condition for action %s",
541 aAction.GetName() ) );
542 }
543 }
544
545 // Update the currently selected action
546 m_toolActions[ groupId ] = &aAction;
547
548 Refresh();
549}
550
551
553{
554 for( int id : m_controlIDs )
555 UpdateControlWidth( id );
556}
557
558
560{
561 wxAuiToolBarItem* item = FindTool( aID );
562 wxASSERT_MSG( item, wxString::Format( "No toolbar item found for ID %d", aID ) );
563
564 // The control on the toolbar is stored inside the window field of the item
565 wxControl* control = dynamic_cast<wxControl*>( item->GetWindow() );
566 wxASSERT_MSG( control, wxString::Format( "No control located in toolbar item with ID %d", aID ) );
567
568 // Update the size the item has stored using the best size of the control
569 control->InvalidateBestSize();
570 wxSize bestSize = control->GetBestSize();
571 item->SetMinSize( bestSize );
572
573 // Update the sizer item sizes
574 // This is a bit convoluted because there are actually 2 sizers that need to be updated:
575 // 1. The main sizer that is used for the entire toolbar (this sizer item can be found in the
576 // toolbar item)
577 if( wxSizerItem* szrItem = item->GetSizerItem() )
578 szrItem->SetMinSize( bestSize );
579
580 // 2. The controls have a second sizer that allows for padding above/below the control with
581 // stretch space, so we also need to update the sizer item for the control in that sizer with
582 // the new size. We let wx do the search for us, since SetItemMinSize is recursive and will
583 // locate the control on that sizer.
584 if( m_sizer )
585 {
586 m_sizer->SetItemMinSize( control, bestSize );
587
588 // Now actually update the toolbar with the new sizes
589 m_sizer->Layout();
590 }
591}
592
593
595{
596 // Clear all the maps keeping track of our items on the toolbar
597 m_toolMenus.clear();
598 m_actionGroups.clear();
599 m_toolCancellable.clear();
600 m_toolKinds.clear();
601 m_toolActions.clear();
602
603 // Remove the actual tools from the toolbar
604 Clear();
605}
606
607
608void ACTION_TOOLBAR::SetToolBitmap( const TOOL_ACTION& aAction, const wxBitmap& aBitmap )
609{
610 int toolId = aAction.GetUIId();
611 wxAuiToolBar::SetToolBitmap( toolId, aBitmap );
612
613 // Set the disabled bitmap: we use the disabled bitmap version of aBitmap.
614 wxAuiToolBarItem* tb_item = wxAuiToolBar::FindTool( toolId );
615
616 if( tb_item )
617 tb_item->SetDisabledBitmap( aBitmap.ConvertToDisabled( KIPLATFORM::UI::IsDarkTheme() ? 70 : 255 ) );
618}
619
620
621void ACTION_TOOLBAR::Toggle( const TOOL_ACTION& aAction, bool aState )
622{
623 int toolId = aAction.GetUIId();
624
625 if( m_toolKinds[ toolId ] )
626 ToggleTool( toolId, aState );
627 else
628 EnableTool( toolId, aState );
629}
630
631
632void ACTION_TOOLBAR::Toggle( const TOOL_ACTION& aAction, bool aEnabled, bool aChecked )
633{
634 int toolId = aAction.GetUIId();
635
636 EnableTool( toolId, aEnabled );
637 ToggleTool( toolId, aEnabled && aChecked );
638}
639
640
641void ACTION_TOOLBAR::onToolEvent( wxAuiToolBarEvent& aEvent )
642{
643 int id = aEvent.GetId();
644 wxEventType type = aEvent.GetEventType();
645 OPT_TOOL_EVENT evt;
646
647 bool handled = false;
648
649 if( m_toolManager && type == wxEVT_COMMAND_TOOL_CLICKED && id >= TOOL_ACTION::GetBaseUIId() )
650 {
651 const auto actionIt = m_toolActions.find( id );
652
653 // The toolbar item is toggled before the event is sent, so we check for it not being
654 // toggled to see if it was toggled originally
655 if( m_toolCancellable[id] && !GetToolToggled( id ) )
656 {
657 // Send a cancel event
658 m_toolManager->CancelTool();
659 handled = true;
660 }
661 else if( actionIt != m_toolActions.end() )
662 {
663 // Dispatch a tool event
664 evt = actionIt->second->MakeEvent();
665 evt->SetHasPosition( false );
666 m_toolManager->ProcessEvent( *evt );
667 m_toolManager->GetToolHolder()->RefreshCanvas();
668 handled = true;
669 }
670 }
671
672 // Skip the event if we don't handle it
673 if( !handled )
674 aEvent.Skip();
675}
676
677
678void ACTION_TOOLBAR::onRightClick( wxAuiToolBarEvent& aEvent )
679{
680 int toolId = aEvent.GetToolId();
681
682 // This means the event was not on a button
683 if( toolId == -1 )
684 return;
685
686 // Ensure that the ID maps to a proper tool ID. If right-clicked on a group item, this is needed
687 // to get the ID of the currently selected action, since the event's ID is that of the group.
688 const auto actionIt = m_toolActions.find( toolId );
689
690 if( actionIt != m_toolActions.end() )
691 toolId = actionIt->second->GetUIId();
692
693 // Find the menu for the action
694 const auto menuIt = m_toolMenus.find( toolId );
695
696 if( menuIt == m_toolMenus.end() )
697 return;
698
699 // Update and show the menu
700 std::unique_ptr<ACTION_MENU>& owningMenu = menuIt->second;
701
702 // Get the actual menu pointer to show it
703 ACTION_MENU* menu = owningMenu.get();
704 SELECTION dummySel;
705
706 if( CONDITIONAL_MENU* condMenu = dynamic_cast<CONDITIONAL_MENU*>( menu ) )
707 condMenu->Evaluate( dummySel );
708
709 menu->UpdateAll();
710 PopupMenu( menu );
711
712 // Remove hovered item when the menu closes, otherwise it remains hovered even if the
713 // mouse is not on the toolbar
714 SetHoverItem( nullptr );
715}
716
717
718// The time (in milliseconds) between pressing the left mouse button and opening the palette
719#define PALETTE_OPEN_DELAY 500
720
721
722void ACTION_TOOLBAR::onMouseClick( wxMouseEvent& aEvent )
723{
724 wxAuiToolBarItem* item = FindToolByPosition( aEvent.GetX(), aEvent.GetY() );
725
726 if( item )
727 {
728 // Ensure there is no active palette
729 if( m_palette )
730 {
731 m_palette->Hide();
732 m_palette->Destroy();
733 m_palette = nullptr;
734 }
735
736 // Start the popup conditions if it is a left mouse click and the tool clicked is a group
737 if( aEvent.LeftDown() && ( m_actionGroups.find( item->GetId() ) != m_actionGroups.end() ) )
739
740 // Clear the popup conditions if it is a left up, because that implies a click happened
741 if( aEvent.LeftUp() )
742 m_paletteTimer->Stop();
743 }
744
745 // Skip the event so wx can continue processing the mouse event
746 aEvent.Skip();
747}
748
749
750void ACTION_TOOLBAR::onItemDrag( wxAuiToolBarEvent& aEvent )
751{
752 int toolId = aEvent.GetToolId();
753
754 if( m_actionGroups.find( toolId ) != m_actionGroups.end() )
755 {
756 wxAuiToolBarItem* item = FindTool( toolId );
757
758 // Use call after because opening the palette from a mouse handler
759 // creates a weird mouse state that causes problems on OSX.
760 CallAfter( &ACTION_TOOLBAR::popupPalette, item );
761
762 // Don't skip this event since we are handling it
763 return;
764 }
765
766 // Skip since we don't care about it
767 aEvent.Skip();
768}
769
770
771void ACTION_TOOLBAR::onTimerDone( wxTimerEvent& aEvent )
772{
773 // We need to search for the tool using the client coordinates
774 wxPoint mousePos = ScreenToClient( KIPLATFORM::UI::GetMousePosition() );
775
776 wxAuiToolBarItem* item = FindToolByPosition( mousePos.x, mousePos.y );
777
778 if( item )
779 popupPalette( item );
780}
781
782
783void ACTION_TOOLBAR::onPaletteEvent( wxCommandEvent& aEvent )
784{
785 if( !m_palette )
786 return;
787
788 OPT_TOOL_EVENT evt;
789 ACTION_GROUP* group = m_palette->GetGroup();
790
791 // Find the action corresponding to the button press
792 auto actionIt = std::find_if( group->GetActions().begin(), group->GetActions().end(),
793 [&]( const TOOL_ACTION* aAction )
794 {
795 return aAction->GetUIId() == aEvent.GetId();
796 } );
797
798 if( actionIt != group->GetActions().end() )
799 {
800 const TOOL_ACTION* action = *actionIt;
801
802 // Dispatch a tool event
803 evt = action->MakeEvent();
804 evt->SetHasPosition( false );
805 m_toolManager->ProcessEvent( *evt );
806 m_toolManager->GetToolHolder()->RefreshCanvas();
807
808 // Update the main toolbar item with the selected action
809 doSelectAction( group, *action );
810 }
811
812 // Hide the palette
813 m_palette->Hide();
814 m_palette->Destroy();
815 m_palette = nullptr;
816}
817
818
819void ACTION_TOOLBAR::popupPalette( wxAuiToolBarItem* aItem )
820{
821 // Clear all popup conditions
822 m_paletteTimer->Stop();
823
824 wxWindow* toolParent = dynamic_cast<wxWindow*>( m_toolManager->GetToolHolder() );
825
826 wxCHECK( GetParent() && m_auiManager && toolParent, /* void */ );
827
828 // Ensure the item we are using for the palette has a group associated with it.
829 const auto it = m_actionGroups.find( aItem->GetId() );
830
831 if( it == m_actionGroups.end() )
832 return;
833
834 ACTION_GROUP* group = it->second.get();
835
836 wxAuiPaneInfo& pane = m_auiManager->GetPane( this );
837
838 // We use the size of the toolbar items for our palette buttons
839 wxRect toolRect = GetToolRect( aItem->GetId() );
840
841 // The position for the palette window must be in screen coordinates
842 wxPoint pos( ClientToScreen( toolRect.GetPosition() ) );
843
844 // True for vertical buttons, false for horizontal
845 bool dir = true;
846 size_t numActions = group->m_actions.size();
847
848 // The size of the palette in the long dimension
849 int paletteLongDim = ( 2 * PALETTE_BORDER ) // The border on all sides of the buttons
850 + ( BUTTON_BORDER ) // The border on the start of the buttons
851 + ( numActions * BUTTON_BORDER ) // The other button borders
852 + ( numActions * toolRect.GetHeight() ); // The size of the buttons
853
854 // Determine the position of the top left corner of the palette window
855 switch( pane.dock_direction )
856 {
857 case wxAUI_DOCK_TOP:
858 // Top toolbars need to shift the palette window down by the toolbar padding
859 dir = true; // Buttons are vertical in the palette
860 pos = ClientToScreen( toolRect.GetBottomLeft() );
861 pos += wxPoint( -PALETTE_BORDER, // Shift left to align the button edges
862 m_bottomPadding ); // Shift down to move away from the toolbar
863 break;
864
865 case wxAUI_DOCK_BOTTOM:
866 // Bottom toolbars need to shift the palette window up by its height (all buttons +
867 // border + toolbar padding)
868 dir = true; // Buttons are vertical in the palette
869 pos = ClientToScreen( toolRect.GetTopLeft() );
870 pos += wxPoint( -PALETTE_BORDER, // Shift left to align the button
871 // Shift up by the entire length of the palette.
872 -( paletteLongDim + m_topPadding ) );
873 break;
874
875 case wxAUI_DOCK_LEFT:
876 // Left toolbars need to shift the palette window up by the toolbar padding
877 dir = false; // Buttons are horizontal in the palette
878 pos = ClientToScreen( toolRect.GetTopRight() );
879 pos += wxPoint( m_rightPadding, // Shift right to move away from the toolbar
880 -( PALETTE_BORDER ) ); // Shift up to align the button tops
881 break;
882
883 case wxAUI_DOCK_RIGHT:
884 // Right toolbars need to shift the palette window left by its width (all buttons +
885 // border + toolbar padding)
886 dir = false; // Buttons are horizontal in the palette
887 pos = ClientToScreen( toolRect.GetTopLeft() );
888
889 // Shift left by the entire length of the palette.
890 pos += wxPoint( -( paletteLongDim + m_leftPadding ),
891 -( PALETTE_BORDER ) ); // Shift up to align the button
892 break;
893 }
894
895 m_palette = new ACTION_TOOLBAR_PALETTE( GetParent(), dir );
896
897 // We handle the button events in the toolbar class, so connect the right handler
898 m_palette->SetGroup( group );
899 m_palette->SetButtonSize( toolRect );
900 m_palette->Connect( wxEVT_BUTTON, wxCommandEventHandler( ACTION_TOOLBAR::onPaletteEvent ), nullptr, this );
901
902
903 // Add the actions in the group to the palette and update their enabled state
904 // We purposely don't check items in the palette
905 for( const TOOL_ACTION* action : group->m_actions )
906 {
907 wxUpdateUIEvent evt( action->GetUIId() );
908
909 toolParent->ProcessWindowEvent( evt );
910
911 m_palette->AddAction( *action );
912
913 if( evt.GetSetEnabled() )
914 m_palette->EnableAction( *action, evt.GetEnabled() );
915 }
916
917 // Release the mouse to ensure the first click will be recognized in the palette
918 ReleaseMouse();
919
920 m_palette->SetPosition( pos );
921 m_palette->Popup();
922
923 // Clear the mouse state on the toolbar because otherwise wxWidgets gets confused
924 // and won't properly display any highlighted items after the palette is closed.
925 // (This is the equivalent of calling the DoResetMouseState() private function)
926 RefreshOverflowState();
927 SetHoverItem( nullptr );
928 SetPressedItem( nullptr );
929
930 m_dragging = false;
931 m_tipItem = nullptr;
932 m_actionPos = wxPoint( -1, -1 );
933 m_actionItem = nullptr;
934}
935
936
937void ACTION_TOOLBAR::OnCustomRender(wxDC& aDc, const wxAuiToolBarItem& aItem, const wxRect& aRect )
938{
939 auto it = m_actionGroups.find( aItem.GetId() );
940
941 if( it == m_actionGroups.end() )
942 return;
943
944 // Choose the color to draw the triangle
945 wxColour clr;
946
947 if( aItem.GetState() & wxAUI_BUTTON_STATE_DISABLED )
948 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
949 else
950 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT );
951
952 // Must set both the pen (for the outline) and the brush (for the polygon fill)
953 aDc.SetPen( wxPen( clr ) );
954 aDc.SetBrush( wxBrush( clr ) );
955
956 // Make the side length of the triangle approximately 1/5th of the bitmap
957 int sideLength = KiROUND( aRect.height / 5.0 );
958
959 // This will create a triangle with its point at the bottom right corner,
960 // and its other two corners along the right and bottom sides
961 wxPoint btmRight = aRect.GetBottomRight();
962 wxPoint topCorner( btmRight.x, btmRight.y - sideLength );
963 wxPoint btmCorner( btmRight.x - sideLength, btmRight.y );
964
965 wxPointList points;
966 points.Append( &btmRight );
967 points.Append( &topCorner );
968 points.Append( &btmCorner );
969
970 aDc.DrawPolygon( &points );
971}
972
973
975{
976#if wxCHECK_VERSION( 3, 3, 0 )
977 return Realize();
978#else
979 wxClientDC dc( this );
980
981 if( !dc.IsOk() )
982 return false;
983
984 // calculate hint sizes for both horizontal and vertical
985 // in the order that leaves toolbar in correct final state
986
987 // however, skip calculating alternate orientations if we don't need them due to window style
988 bool retval = true;
989
990 if( m_orientation == wxHORIZONTAL )
991 {
992 if( !( GetWindowStyle() & wxAUI_TB_HORIZONTAL ) )
993 {
994 m_vertHintSize = GetSize();
995 retval = RealizeHelper( dc, false );
996 }
997
998 if( retval && RealizeHelper( dc, true ) )
999 m_horzHintSize = GetSize();
1000 else
1001 retval = false;
1002 }
1003 else
1004 {
1005 if( !( GetWindowStyle() & wxAUI_TB_VERTICAL ) )
1006 {
1007 m_horzHintSize = GetSize();
1008 retval = RealizeHelper( dc, true );
1009 }
1010
1011 if( retval && RealizeHelper( dc, false ) )
1012 m_vertHintSize = GetSize();
1013 else
1014 retval = false;
1015 }
1016
1017 Refresh( false );
1018 return retval;
1019#endif
1020}
1021
1022
1023void ACTION_TOOLBAR::onThemeChanged( wxSysColourChangedEvent &aEvent )
1024{
1027
1028 aEvent.Skip();
1029}
1030
1031
1033{
1034 for( const std::pair<int, const TOOL_ACTION*> pair : m_toolActions )
1035 {
1036 wxAuiToolBarItem* tool = FindTool( pair.first );
1037
1038 tool->SetBitmap( KiBitmapBundle( pair.second->GetIcon(),
1039 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ) );
1040 tool->SetDisabledBitmap( KiDisabledBitmapBundle( pair.second->GetIcon() ) );
1041 }
1042
1043 Refresh();
1044}
1045
1046/*
1047 * Common controls for the toolbar
1048 */
1049ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::gridSelect( "control.GridSelector", _( "Grid Selector" ),
1050 _( "Grid Selection box" ) );
1051
1052ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::zoomSelect( "control.ZoomSelector", _( "Zoom Selector" ),
1053 _( "Zoom Selection box" ) );
1054
1055ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::ipcScripting( "control.IPCPlugin", _( "IPC/Scripting plugins" ),
1056 _( "Region to hold the IPC/Scripting action buttons" ) );
1057
1058ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::layerSelector( "control.LayerSelector", _( "Layer selector" ),
1059 _( "Control to select the layer" ) );
1060
1061ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::unitSelector( "control.UnitSelector", _( "Symbol unit selector" ),
1062 _( "Displays the current unit" ) );
1063
1065 _( "Symbol body style selector" ),
1066 _( "Displays the current body style" ) );
1067
1068ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::overrideLocks( "control.OverrideLocks", _( "Override locks" ),
1069 _( "Allow moving of locked items with the mouse" ) );
#define PALETTE_BORDER
#define PALETTE_OPEN_DELAY
#define BUTTON_BORDER
std::function< void(ACTION_TOOLBAR *)> ACTION_TOOLBAR_CONTROL_FACTORY
Type for the function signature that is used to add custom controls to the toolbar.
int KiIconScale(wxWindow *aWindow)
Return the automatic scale factor that would be used for a given window by KiScaledBitmap and KiScale...
Definition bitmap.cpp:122
BITMAP_STORE * GetBitmapStore()
Definition bitmap.cpp:92
wxBitmapBundle KiDisabledBitmapBundle(BITMAPS aBitmap)
Definition bitmap.cpp:116
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
Definition bitmap.cpp:110
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
A group of actions that will be displayed together on a toolbar palette.
void SetDefaultAction(const TOOL_ACTION &aDefault)
Set the default action to use when first creating the toolbar palette icon.
ACTION_GROUP(const std::string_view &aName)
std::vector< const TOOL_ACTION * > m_actions
void SetActions(const std::vector< const TOOL_ACTION * > &aActions)
Set the actions contained in this group.
int GetUIId() const
Get the ID used in the UI to reference this group.
int m_id
< The action ID for this action group
const TOOL_ACTION * m_defaultAction
The actions that compose the group. Non-owning.
std::string m_name
The default action to display on the toolbar item.
static int MakeActionId(const std::string &aActionName)
Generate an unique ID from for an action with given name.
Define the structure of a menu based on ACTIONs.
Definition action_menu.h:47
void UpdateAll()
Run update handlers for the menu and its submenus.
static ACTION_TOOLBAR_CONTROL gridSelect
static ACTION_TOOLBAR_CONTROL overrideLocks
static ACTION_TOOLBAR_CONTROL layerSelector
static ACTION_TOOLBAR_CONTROL zoomSelect
static ACTION_TOOLBAR_CONTROL unitSelector
static ACTION_TOOLBAR_CONTROL ipcScripting
static ACTION_TOOLBAR_CONTROL bodyStyleSelector
Class to hold basic information about controls that can be added to the toolbars.
A popup window that contains a row of toolbar-like buttons for the user to choose from.
void CheckAction(const TOOL_ACTION &aAction, bool aCheck=true)
Check/Toggle the button for an action on the palette.
void onCharHook(wxKeyEvent &aEvent)
wxBoxSizer * m_buttonSizer
The buttons that act as the toolbar on the palette.
void AddAction(const TOOL_ACTION &aAction)
Add an action to the palette.
ACTION_TOOLBAR_PALETTE(wxWindow *aParent, bool aVertical)
Create the palette.
wxRect m_buttonSize
True if the palette uses vertical buttons, false for horizontal buttons.
void EnableAction(const TOOL_ACTION &aAction, bool aEnable=true)
Enable the button for an action on the palette.
ACTION_GROUP * m_group
The size each button on the toolbar should be.
std::map< int, BITMAP_BUTTON * > m_buttons
void Popup(wxWindow *aFocus=nullptr) override
Popup this window.
void RefreshBitmaps()
Reload all the bitmaps for the tools (e.g.
void OnCustomRender(wxDC &aDc, const wxAuiToolBarItem &aItem, const wxRect &aRect) override
void onTimerDone(wxTimerEvent &aEvent)
void doSelectAction(ACTION_GROUP *aGroup, const TOOL_ACTION &aAction)
Update a group toolbar item to look like a specific action.
void onMouseClick(wxMouseEvent &aEvent)
Handler for when a drag event occurs on an item.
void AddButton(const TOOL_ACTION &aAction)
Add a large button such as used in the KiCad Manager Frame's launch bar.
wxAuiManager * m_auiManager
void UpdateControlWidth(int aID)
Update the toolbar item width of a control using its best size.
ACTION_TOOLBAR(EDA_BASE_FRAME *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxAUI_TB_DEFAULT_STYLE)
void Toggle(const TOOL_ACTION &aAction, bool aState)
Apply the default toggle action.
void SelectAction(ACTION_GROUP *aGroup, const TOOL_ACTION &aAction)
Select an action inside a group.
void Add(const TOOL_ACTION &aAction)
Add a TOOL_ACTION-based button to the toolbar.
void onPaletteEvent(wxCommandEvent &aEvent)
Handle the palette timer triggering.
void onRightClick(wxAuiToolBarEvent &aEvent)
Handle the button select inside the palette.
wxTimer * m_paletteTimer
std::map< int, std::unique_ptr< ACTION_MENU > > m_toolMenus
void onToolEvent(wxAuiToolBarEvent &aEvent)
Handle a right-click on a menu item.
void onItemDrag(wxAuiToolBarEvent &aEvent)
The default tool event handler.
std::map< int, bool > m_toolKinds
std::map< int, std::unique_ptr< ACTION_GROUP > > m_actionGroups
void AddGroup(std::unique_ptr< ACTION_GROUP > aGroup)
Add a set of actions to a toolbar as a group.
void AddToolContextMenu(const TOOL_ACTION &aAction, std::unique_ptr< ACTION_MENU > aMenu)
Add a context menu to a specific tool item on the toolbar.
std::map< int, bool > m_toolCancellable
void AddScaledSeparator(wxWindow *aWindow)
Add a separator that introduces space on either side to not squash the tools when scaled.
bool KiRealize()
Use this over Realize() to avoid a rendering glitch with fixed orientation toolbars.
void popupPalette(wxAuiToolBarItem *aItem)
Popup the ACTION_TOOLBAR_PALETTE associated with the ACTION_GROUP of the given toolbar item.
virtual ~ACTION_TOOLBAR()
ACTION_TOOLBAR_PALETTE * m_palette
std::vector< int > m_controlIDs
IDs for all the control items in this toolbar.
void onThemeChanged(wxSysColourChangedEvent &aEvent)
Render the triangle in the lower-right corner that represents that an action palette is available for...
std::map< int, const TOOL_ACTION * > m_toolActions
void ClearToolbar()
Clear the toolbar and remove all associated menus.
void SetToolBitmap(const TOOL_ACTION &aAction, const wxBitmap &aBitmap)
Updates the bitmap of a particular tool.
void UpdateControlWidths()
Update the width of all wxControl tools on thsi toolbar.
void ApplyConfiguration(const TOOLBAR_CONFIGURATION &aConfig)
Replace the contents of this toolbar with the configuration given in aConfig.
TOOL_MANAGER * m_toolManager
A bitmap button widget that behaves like an AUI toolbar item's button when it is drawn.
void AcceptDragInAsClick(bool aAcceptDragIn=true)
Accept mouse-up as click even if mouse-down happened outside of the control.
void SetDisabledBitmap(const wxBitmapBundle &aBmp)
Set the bitmap shown when the button is disabled.
void SetBitmapCentered(bool aCentered=true)
void SetIsToolbarButton(bool aIsToolbar=true)
void SetBitmap(const wxBitmapBundle &aBmp)
Set the bitmap shown when the button is enabled.
void SetPadding(int aPadding)
Set the amount of padding present on each side of the bitmap.
void ThemeChanged()
Notifies the store that the icon theme has been changed by the user, so caches must be invalidated.
APPEARANCE m_Appearance
The base frame for deriving all KiCad main window classes.
ACTION_TOOLBAR_CONTROL_FACTORY * GetCustomToolbarControlFactory(const std::string &aName)
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:576
std::vector< TOOLBAR_ITEM > GetToolbarItems() const
std::vector< TOOLBAR_ITEM > m_GroupItems
std::string m_ActionName
TOOLBAR_ITEM_TYPE m_Type
Represent a single user action.
static int GetBaseUIId()
Get the base value used to offset the user interface IDs for the actions.
BITMAPS GetIcon() const
Return an icon associated with the action.
const std::string & GetName() const
Return name of the action.
bool CheckToolbarState(TOOLBAR_STATE aState) const
Check if a specific toolbar state is required for this action.
TOOL_EVENT MakeEvent() const
Return the event associated with the action (i.e.
wxString GetButtonTooltip() const
int GetUIId() const
Get the unique ID for this action in the user interface system.
void SetHasPosition(bool aHasPosition)
Definition tool_event.h:261
#define _(s)
const wxChar *const kicadTraceToolStack
Flag to enable tracing of the tool handling stack.
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition wxgtk/ui.cpp:689
double GetContentScaleFactor(const wxWindow *aWindow)
Tries to determine the content scaling factor currently in use for the window.
Definition wxgtk/ui.cpp:257
bool IsDarkTheme()
Determine if the desktop interface is currently using a dark theme or a light theme.
Definition wxgtk/ui.cpp:48
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
PGM_BASE & Pgm()
The global program "get" accessor.
Definition pgm_base.cpp:913
see class PGM_BASE
const int scale
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
@ TOGGLE
Action is a toggle button on the toolbar.
Definition tool_action.h:64
@ CANCEL
Action can be cancelled by clicking the toolbar button again.
Definition tool_action.h:65
@ HIDDEN
Action is hidden from the toolbar.
Definition tool_action.h:63
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition tool_event.h:641
wxLogTrace helper definitions.