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,
206 const wxSize& size, 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 ),
218 nullptr, this );
219 Connect( wxEVT_AUITOOLBAR_RIGHT_CLICK,
220 wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolRightClick ),
221 nullptr, this );
222 Connect( wxEVT_AUITOOLBAR_BEGIN_DRAG, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onItemDrag ),
223 nullptr, this );
224 Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
225 Connect( wxEVT_LEFT_UP, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
226 Connect( m_paletteTimer->GetId(), wxEVT_TIMER,
227 wxTimerEventHandler( ACTION_TOOLBAR::onTimerDone ), nullptr, this );
228
229 Bind( wxEVT_SYS_COLOUR_CHANGED,
230 wxSysColourChangedEventHandler( ACTION_TOOLBAR::onThemeChanged ), this );
231}
232
233
235{
236 Disconnect( wxEVT_COMMAND_TOOL_CLICKED, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolEvent ),
237 nullptr, this );
238 Disconnect( wxEVT_AUITOOLBAR_RIGHT_CLICK,
239 wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolRightClick ), nullptr, this );
240 Disconnect( wxEVT_AUITOOLBAR_BEGIN_DRAG, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onItemDrag ),
241 nullptr, this );
242 Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr,
243 this );
244 Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
245 Disconnect( m_paletteTimer->GetId(), wxEVT_TIMER,
246 wxTimerEventHandler( ACTION_TOOLBAR::onTimerDone ), nullptr, this );
247
248 Unbind( wxEVT_SYS_COLOUR_CHANGED,
249 wxSysColourChangedEventHandler( ACTION_TOOLBAR::onThemeChanged ), this );
250
251 delete m_paletteTimer;
252
253 // Clear all the maps keeping track of our items on the toolbar
254 m_toolMenus.clear();
255 m_actionGroups.clear();
256 m_toolCancellable.clear();
257 m_toolKinds.clear();
258 m_toolActions.clear();
259}
260
261
263{
264 wxASSERT( GetParent() );
265
266 std::map<std::string, std::string> currentGroupItems;
267
268 for( const auto& [id, group] : m_actionGroups )
269 {
270 if( m_toolActions[group->GetUIId()] )
271 currentGroupItems[group->GetName()] = m_toolActions[group->GetUIId()]->GetName();
272 }
273
274 // Remove existing tools
275 ClearToolbar();
276
277 std::vector<TOOLBAR_ITEM> items = aConfig.GetToolbarItems();
278
279 // Add all the items to the toolbar
280 for( auto& item : items )
281 {
282 switch( item.m_Type )
283 {
284 case TOOLBAR_ITEM_TYPE::SEPARATOR:
285 AddScaledSeparator( GetParent() );
286 break;
287
288 case TOOLBAR_ITEM_TYPE::SPACER:
289 AddSpacer( item.m_Size );
290 break;
291
292 case TOOLBAR_ITEM_TYPE::TB_GROUP:
293 {
294 // Add a group of items to the toolbar
295 std::string groupName = item.m_GroupName.ToStdString();
296 std::vector<const TOOL_ACTION*> tools;
297 const TOOL_ACTION* defaultTool = nullptr;
298
299 for( TOOLBAR_ITEM& groupItem : item.m_GroupItems )
300 {
301 switch( groupItem.m_Type )
302 {
303 case TOOLBAR_ITEM_TYPE::SEPARATOR:
304 case TOOLBAR_ITEM_TYPE::SPACER:
305 case TOOLBAR_ITEM_TYPE::TB_GROUP:
306 case TOOLBAR_ITEM_TYPE::CONTROL:
307 wxFAIL_MSG( wxT( "Unsupported group item type" ) );
308 continue;
309
310 case TOOLBAR_ITEM_TYPE::TOOL:
312
313 if( !grpAction )
314 {
315 wxFAIL_MSG( wxString::Format( wxT( "Unable to find group tool %s" ), groupItem.m_ActionName ) );
316 continue;
317 }
318
319 tools.push_back( grpAction );
320
321 if( currentGroupItems[groupName] == groupItem.m_ActionName )
322 defaultTool = grpAction;
323 }
324 }
325
326 std::unique_ptr<ACTION_GROUP> group = std::make_unique<ACTION_GROUP>( groupName, tools );
327
328 if( defaultTool )
329 group->SetDefaultAction( *defaultTool );
330
331 AddGroup( std::move( group ) );
332 break;
333 }
334
335 case TOOLBAR_ITEM_TYPE::CONTROL:
336 {
337 // Add a custom control to the toolbar
338 EDA_BASE_FRAME* frame = static_cast<EDA_BASE_FRAME*>( GetParent() );
339 ACTION_TOOLBAR_CONTROL_FACTORY* factory = frame->GetCustomToolbarControlFactory( item.m_ControlName );
340
341 if( !factory )
342 {
343 wxFAIL_MSG( wxString::Format( wxT( "Unable to find control factory for %s" ), item.m_ControlName ) );
344 continue;
345 }
346
347 // The factory functions are responsible for adding the controls to the toolbar themselves
348 (*factory)( this );
349 break;
350 }
351
352 case TOOLBAR_ITEM_TYPE::TOOL:
353 {
354 TOOL_ACTION* action = m_toolManager->GetActionManager()->FindAction( item.m_ActionName );
355
356 if( !action )
357 {
358 wxFAIL_MSG( wxString::Format( wxT( "Unable to find toolbar tool %s" ), item.m_ActionName ) );
359 continue;
360 }
361
362 Add( *action );
363 break;
364 }
365 }
366 }
367
368 // Apply the configuration
369 KiRealize();
370}
371
372
373void ACTION_TOOLBAR::Add( const TOOL_ACTION& aAction )
374{
375 wxASSERT_MSG( !aAction.CheckToolbarState( TOOLBAR_STATE::HIDDEN ),
376 wxString::Format( "Attempting to add hidden action %s to the toolbar", aAction.GetName() ) );
377
378 bool isToggleEntry = aAction.CheckToolbarState( TOOLBAR_STATE::TOGGLE );
379 bool isCancellable = aAction.CheckToolbarState( TOOLBAR_STATE::CANCEL );
380
381 Add( aAction, isToggleEntry, isCancellable );
382}
383
384
385void ACTION_TOOLBAR::Add( const TOOL_ACTION& aAction, bool aIsToggleEntry, bool aIsCancellable )
386{
387 wxASSERT( GetParent() );
388 wxASSERT_MSG( !( aIsCancellable && !aIsToggleEntry ),
389 wxS( "aIsCancellable requires aIsToggleEntry" ) );
390
391 int toolId = aAction.GetUIId();
392
393 AddTool( toolId, wxEmptyString,
394 KiBitmapBundle( aAction.GetIcon(),
395 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ),
396 KiDisabledBitmapBundle( aAction.GetIcon() ),
397 aIsToggleEntry ? wxITEM_CHECK : wxITEM_NORMAL,
398 aAction.GetButtonTooltip(), wxEmptyString, nullptr );
399
400 m_toolKinds[ toolId ] = aIsToggleEntry;
401 m_toolActions[ toolId ] = &aAction;
402 m_toolCancellable[ toolId ] = aIsCancellable;
403}
404
405
407{
408 int toolId = aAction.GetUIId();
409
410 AddTool( toolId, wxEmptyString,
411 KiBitmapBundle( aAction.GetIcon(),
412 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ),
413 KiDisabledBitmapBundle( aAction.GetIcon() ), wxITEM_NORMAL,
414 aAction.GetButtonTooltip(), wxEmptyString, nullptr );
415
416 m_toolKinds[ toolId ] = false;
417 m_toolActions[ toolId ] = &aAction;
418}
419
420
421void ACTION_TOOLBAR::AddScaledSeparator( wxWindow* aWindow )
422{
423 int scale = KiIconScale( aWindow );
424
425 if( scale > 4 )
426 AddSpacer( 16 * ( scale - 4 ) / 4 );
427
428 AddSeparator();
429
430 if( scale > 4 )
431 AddSpacer( 16 * ( scale - 4 ) / 4 );
432}
433
434
435void ACTION_TOOLBAR::Add( wxControl* aControl, const wxString& aLabel )
436{
437 wxASSERT( aControl );
438 m_controlIDs.push_back( aControl->GetId() );
439 AddControl( aControl, aLabel );
440}
441
442
444 std::unique_ptr<ACTION_MENU> aMenu )
445{
446 int toolId = aAction.GetUIId();
447
448 m_toolMenus[toolId] = std::move( aMenu );
449}
450
451
452void ACTION_TOOLBAR::AddGroup( std::unique_ptr<ACTION_GROUP> aGroup )
453{
454 int groupId = aGroup->GetUIId();
455 const TOOL_ACTION* defaultAction = aGroup->GetDefaultAction();
456
457 wxASSERT( GetParent() );
458 wxASSERT( defaultAction );
459
460 // Turn this into a toggle entry if any one of the actions is a toggle entry
461 bool isToggleEntry = false;
462
463 for( const auto& act : aGroup->GetActions() )
464 isToggleEntry |= act->CheckToolbarState( TOOLBAR_STATE::TOGGLE );
465
466
467 m_toolKinds[ groupId ] = isToggleEntry;
468 m_toolActions[ groupId ] = defaultAction;
469 m_actionGroups[ groupId ] = std::move( aGroup );
470
471 // Add the main toolbar item representing the group
472 AddTool( groupId, wxEmptyString,
473 KiBitmapBundle( defaultAction->GetIcon(),
474 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ),
475 KiDisabledBitmapBundle( defaultAction->GetIcon() ),
476 isToggleEntry ? wxITEM_CHECK : wxITEM_NORMAL,
477 wxEmptyString, wxEmptyString, nullptr );
478
479 // Select the default action
480 doSelectAction( m_actionGroups[ groupId ].get(), *defaultAction );
481}
482
483
485{
486 bool valid = std::any_of( aGroup->m_actions.begin(), aGroup->m_actions.end(),
487 [&]( const TOOL_ACTION* action2 ) -> bool
488 {
489 // For some reason, we can't compare the actions directly
490 return aAction.GetId() == action2->GetId();
491 } );
492
493 if( valid )
494 doSelectAction( aGroup, aAction );
495}
496
497
499{
500 wxASSERT( GetParent() );
501
502 int groupId = aGroup->GetUIId();
503
504 wxAuiToolBarItem* item = FindTool( groupId );
505
506 if( !item )
507 return;
508
509 // Update the item information
510 item->SetShortHelp( aAction.GetButtonTooltip() );
511 item->SetBitmap( KiBitmapBundle( aAction.GetIcon(),
512 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ) );
513 item->SetDisabledBitmap( KiDisabledBitmapBundle( aAction.GetIcon() ) );
514
515 // Register a new handler with the new UI conditions
516 if( m_toolManager )
517 {
519
521
522 // Register the new UI condition to control this entry
523 if( cond )
524 {
526 }
527 else
528 {
529 wxLogTrace( kicadTraceToolStack, wxString::Format( "No UI condition for action %s",
530 aAction.GetName() ) );
531 }
532 }
533
534 // Update the currently selected action
535 m_toolActions[ groupId ] = &aAction;
536
537 Refresh();
538}
539
540
542{
543 for( int id : m_controlIDs )
544 UpdateControlWidth( id );
545}
546
547
549{
550 wxAuiToolBarItem* item = FindTool( aID );
551 wxASSERT_MSG( item, wxString::Format( "No toolbar item found for ID %d", aID ) );
552
553 // The control on the toolbar is stored inside the window field of the item
554 wxControl* control = dynamic_cast<wxControl*>( item->GetWindow() );
555 wxASSERT_MSG( control,
556 wxString::Format( "No control located in toolbar item with ID %d", aID ) );
557
558 // Update the size the item has stored using the best size of the control
559 control->InvalidateBestSize();
560 wxSize bestSize = control->GetBestSize();
561 item->SetMinSize( bestSize );
562
563 // Update the sizer item sizes
564 // This is a bit convoluted because there are actually 2 sizers that need to be updated:
565 // 1. The main sizer that is used for the entire toolbar (this sizer item can be found in the
566 // toolbar item)
567 if( wxSizerItem* szrItem = item->GetSizerItem() )
568 szrItem->SetMinSize( bestSize );
569
570 // 2. The controls have a second sizer that allows for padding above/below the control with
571 // stretch space, so we also need to update the sizer item for the control in that sizer with
572 // the new size. We let wx do the search for us, since SetItemMinSize is recursive and will
573 // locate the control on that sizer.
574 if( m_sizer )
575 {
576 m_sizer->SetItemMinSize( control, bestSize );
577
578 // Now actually update the toolbar with the new sizes
579 m_sizer->Layout();
580 }
581}
582
583
585{
586 // Clear all the maps keeping track of our items on the toolbar
587 m_toolMenus.clear();
588 m_actionGroups.clear();
589 m_toolCancellable.clear();
590 m_toolKinds.clear();
591 m_toolActions.clear();
592
593 // Remove the actual tools from the toolbar
594 Clear();
595}
596
597
598void ACTION_TOOLBAR::SetToolBitmap( const TOOL_ACTION& aAction, const wxBitmap& aBitmap )
599{
600 int toolId = aAction.GetUIId();
601 wxAuiToolBar::SetToolBitmap( toolId, aBitmap );
602
603 // Set the disabled bitmap: we use the disabled bitmap version of aBitmap.
604 wxAuiToolBarItem* tb_item = wxAuiToolBar::FindTool( toolId );
605
606 if( tb_item )
607 {
608 tb_item->SetDisabledBitmap(
609 aBitmap.ConvertToDisabled( KIPLATFORM::UI::IsDarkTheme() ? 70 : 255 ) );
610 }
611}
612
613
614void ACTION_TOOLBAR::Toggle( const TOOL_ACTION& aAction, bool aState )
615{
616 int toolId = aAction.GetUIId();
617
618 if( m_toolKinds[ toolId ] )
619 ToggleTool( toolId, aState );
620 else
621 EnableTool( toolId, aState );
622}
623
624
625void ACTION_TOOLBAR::Toggle( const TOOL_ACTION& aAction, bool aEnabled, bool aChecked )
626{
627 int toolId = aAction.GetUIId();
628
629 EnableTool( toolId, aEnabled );
630 ToggleTool( toolId, aEnabled && aChecked );
631}
632
633
634void ACTION_TOOLBAR::onToolEvent( wxAuiToolBarEvent& aEvent )
635{
636 int id = aEvent.GetId();
637 wxEventType type = aEvent.GetEventType();
638 OPT_TOOL_EVENT evt;
639
640 bool handled = false;
641
642 if( m_toolManager && type == wxEVT_COMMAND_TOOL_CLICKED && id >= TOOL_ACTION::GetBaseUIId() )
643 {
644 const auto actionIt = m_toolActions.find( id );
645
646 // The toolbar item is toggled before the event is sent, so we check for it not being
647 // toggled to see if it was toggled originally
648 if( m_toolCancellable[id] && !GetToolToggled( id ) )
649 {
650 // Send a cancel event
652 handled = true;
653 }
654 else if( actionIt != m_toolActions.end() )
655 {
656 // Dispatch a tool event
657 evt = actionIt->second->MakeEvent();
658 evt->SetHasPosition( false );
661 handled = true;
662 }
663 }
664
665 // Skip the event if we don't handle it
666 if( !handled )
667 aEvent.Skip();
668}
669
670
671void ACTION_TOOLBAR::onToolRightClick( wxAuiToolBarEvent& aEvent )
672{
673 int toolId = aEvent.GetToolId();
674
675 // This means the event was not on a button
676 if( toolId == -1 )
677 return;
678
679 // Ensure that the ID used maps to a proper tool ID.
680 // If right-clicked on a group item, this is needed to get the ID of the currently selected
681 // action, since the event's ID is that of the group.
682 const auto actionIt = m_toolActions.find( toolId );
683
684 if( actionIt != m_toolActions.end() )
685 toolId = actionIt->second->GetUIId();
686
687 // Find the menu for the action
688 const auto menuIt = m_toolMenus.find( toolId );
689
690 if( menuIt == m_toolMenus.end() )
691 return;
692
693 // Update and show the menu
694 std::unique_ptr<ACTION_MENU>& owningMenu = menuIt->second;
695
696 // Get the actual menu pointer to show it
697 ACTION_MENU* menu = owningMenu.get();
698 SELECTION dummySel;
699
700 if( CONDITIONAL_MENU* condMenu = dynamic_cast<CONDITIONAL_MENU*>( menu ) )
701 condMenu->Evaluate( dummySel );
702
703 menu->UpdateAll();
704 PopupMenu( menu );
705
706 // Remove hovered item when the menu closes, otherwise it remains hovered even if the
707 // mouse is not on the toolbar
708 SetHoverItem( nullptr );
709}
710
711
712// The time (in milliseconds) between pressing the left mouse button and opening the palette
713#define PALETTE_OPEN_DELAY 500
714
715
716void ACTION_TOOLBAR::onMouseClick( wxMouseEvent& aEvent )
717{
718 wxAuiToolBarItem* item = FindToolByPosition( aEvent.GetX(), aEvent.GetY() );
719
720 if( item )
721 {
722 // Ensure there is no active palette
723 if( m_palette )
724 {
725 m_palette->Hide();
726 m_palette->Destroy();
727 m_palette = nullptr;
728 }
729
730 // Start the popup conditions if it is a left mouse click and the tool clicked is a group
731 if( aEvent.LeftDown() && ( m_actionGroups.find( item->GetId() ) != m_actionGroups.end() ) )
733
734 // Clear the popup conditions if it is a left up, because that implies a click happened
735 if( aEvent.LeftUp() )
736 m_paletteTimer->Stop();
737 }
738
739 // Skip the event so wx can continue processing the mouse event
740 aEvent.Skip();
741}
742
743
744void ACTION_TOOLBAR::onItemDrag( wxAuiToolBarEvent& aEvent )
745{
746 int toolId = aEvent.GetToolId();
747
748 if( m_actionGroups.find( toolId ) != m_actionGroups.end() )
749 {
750 wxAuiToolBarItem* item = FindTool( toolId );
751
752 // Use call after because opening the palette from a mouse handler
753 // creates a weird mouse state that causes problems on OSX.
754 CallAfter( &ACTION_TOOLBAR::popupPalette, item );
755
756 // Don't skip this event since we are handling it
757 return;
758 }
759
760 // Skip since we don't care about it
761 aEvent.Skip();
762}
763
764
765void ACTION_TOOLBAR::onTimerDone( wxTimerEvent& aEvent )
766{
767 // We need to search for the tool using the client coordinates
768 wxPoint mousePos = ScreenToClient( KIPLATFORM::UI::GetMousePosition() );
769
770 wxAuiToolBarItem* item = FindToolByPosition( mousePos.x, mousePos.y );
771
772 if( item )
773 popupPalette( item );
774}
775
776
777void ACTION_TOOLBAR::onPaletteEvent( wxCommandEvent& aEvent )
778{
779 if( !m_palette )
780 return;
781
782 OPT_TOOL_EVENT evt;
784
785 // Find the action corresponding to the button press
786 auto actionIt = std::find_if( group->GetActions().begin(), group->GetActions().end(),
787 [&]( const TOOL_ACTION* aAction )
788 {
789 return aAction->GetUIId() == aEvent.GetId();
790 } );
791
792 if( actionIt != group->GetActions().end() )
793 {
794 const TOOL_ACTION* action = *actionIt;
795
796 // Dispatch a tool event
797 evt = action->MakeEvent();
798 evt->SetHasPosition( false );
801
802 // Update the main toolbar item with the selected action
803 doSelectAction( group, *action );
804 }
805
806 // Hide the palette
807 m_palette->Hide();
808 m_palette->Destroy();
809 m_palette = nullptr;
810}
811
812
813void ACTION_TOOLBAR::popupPalette( wxAuiToolBarItem* aItem )
814{
815 // Clear all popup conditions
816 m_paletteTimer->Stop();
817
818 wxWindow* toolParent = dynamic_cast<wxWindow*>( m_toolManager->GetToolHolder() );
819
820 wxCHECK( GetParent() && m_auiManager && toolParent, /* void */ );
821
822 // Ensure the item we are using for the palette has a group associated with it.
823 const auto it = m_actionGroups.find( aItem->GetId() );
824
825 if( it == m_actionGroups.end() )
826 return;
827
828 ACTION_GROUP* group = it->second.get();
829
830 wxAuiPaneInfo& pane = m_auiManager->GetPane( this );
831
832 // We use the size of the toolbar items for our palette buttons
833 wxRect toolRect = GetToolRect( aItem->GetId() );
834
835 // The position for the palette window must be in screen coordinates
836 wxPoint pos( ClientToScreen( toolRect.GetPosition() ) );
837
838 // True for vertical buttons, false for horizontal
839 bool dir = true;
840 size_t numActions = group->m_actions.size();
841
842 // The size of the palette in the long dimension
843 int paletteLongDim = ( 2 * PALETTE_BORDER ) // The border on all sides of the buttons
844 + ( BUTTON_BORDER ) // The border on the start of the buttons
845 + ( numActions * BUTTON_BORDER ) // The other button borders
846 + ( numActions * toolRect.GetHeight() ); // The size of the buttons
847
848 // Determine the position of the top left corner of the palette window
849 switch( pane.dock_direction )
850 {
851 case wxAUI_DOCK_TOP:
852 // Top toolbars need to shift the palette window down by the toolbar padding
853 dir = true; // Buttons are vertical in the palette
854 pos = ClientToScreen( toolRect.GetBottomLeft() );
855 pos += wxPoint( -PALETTE_BORDER, // Shift left to align the button edges
856 m_bottomPadding ); // Shift down to move away from the toolbar
857 break;
858
859 case wxAUI_DOCK_BOTTOM:
860 // Bottom toolbars need to shift the palette window up by its height (all buttons +
861 // border + toolbar padding)
862 dir = true; // Buttons are vertical in the palette
863 pos = ClientToScreen( toolRect.GetTopLeft() );
864 pos += wxPoint( -PALETTE_BORDER, // Shift left to align the button
865 // Shift up by the entire length of the palette.
866 -( paletteLongDim + m_topPadding ) );
867 break;
868
869 case wxAUI_DOCK_LEFT:
870 // Left toolbars need to shift the palette window up by the toolbar padding
871 dir = false; // Buttons are horizontal in the palette
872 pos = ClientToScreen( toolRect.GetTopRight() );
873 pos += wxPoint( m_rightPadding, // Shift right to move away from the toolbar
874 -( PALETTE_BORDER ) ); // Shift up to align the button tops
875 break;
876
877 case wxAUI_DOCK_RIGHT:
878 // Right toolbars need to shift the palette window left by its width (all buttons +
879 // border + toolbar padding)
880 dir = false; // Buttons are horizontal in the palette
881 pos = ClientToScreen( toolRect.GetTopLeft() );
882
883 // Shift left by the entire length of the palette.
884 pos += wxPoint( -( paletteLongDim + m_leftPadding ),
885 -( PALETTE_BORDER ) ); // Shift up to align the button
886 break;
887 }
888
889 m_palette = new ACTION_TOOLBAR_PALETTE( GetParent(), dir );
890
891 // We handle the button events in the toolbar class, so connect the right handler
893 m_palette->SetButtonSize( toolRect );
894 m_palette->Connect( wxEVT_BUTTON, wxCommandEventHandler( ACTION_TOOLBAR::onPaletteEvent ),
895 nullptr, this );
896
897
898 // Add the actions in the group to the palette and update their enabled state
899 // We purposely don't check items in the palette
900 for( const TOOL_ACTION* action : group->m_actions )
901 {
902 wxUpdateUIEvent evt( action->GetUIId() );
903
904 toolParent->ProcessWindowEvent( evt );
905
906 m_palette->AddAction( *action );
907
908 if( evt.GetSetEnabled() )
909 m_palette->EnableAction( *action, evt.GetEnabled() );
910 }
911
912 // Release the mouse to ensure the first click will be recognized in the palette
913 ReleaseMouse();
914
915 m_palette->SetPosition( pos );
916 m_palette->Popup();
917
918 // Clear the mouse state on the toolbar because otherwise wxWidgets gets confused
919 // and won't properly display any highlighted items after the palette is closed.
920 // (This is the equivalent of calling the DoResetMouseState() private function)
921 RefreshOverflowState();
922 SetHoverItem( nullptr );
923 SetPressedItem( nullptr );
924
925 m_dragging = false;
926 m_tipItem = nullptr;
927 m_actionPos = wxPoint( -1, -1 );
928 m_actionItem = nullptr;
929}
930
931
932void ACTION_TOOLBAR::OnCustomRender(wxDC& aDc, const wxAuiToolBarItem& aItem, const wxRect& aRect )
933{
934 auto it = m_actionGroups.find( aItem.GetId() );
935
936 if( it == m_actionGroups.end() )
937 return;
938
939 // Choose the color to draw the triangle
940 wxColour clr;
941
942 if( aItem.GetState() & wxAUI_BUTTON_STATE_DISABLED )
943 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
944 else
945 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT );
946
947 // Must set both the pen (for the outline) and the brush (for the polygon fill)
948 aDc.SetPen( wxPen( clr ) );
949 aDc.SetBrush( wxBrush( clr ) );
950
951 // Make the side length of the triangle approximately 1/5th of the bitmap
952 int sideLength = KiROUND( aRect.height / 5.0 );
953
954 // This will create a triangle with its point at the bottom right corner,
955 // and its other two corners along the right and bottom sides
956 wxPoint btmRight = aRect.GetBottomRight();
957 wxPoint topCorner( btmRight.x, btmRight.y - sideLength );
958 wxPoint btmCorner( btmRight.x - sideLength, btmRight.y );
959
960 wxPointList points;
961 points.Append( &btmRight );
962 points.Append( &topCorner );
963 points.Append( &btmCorner );
964
965 aDc.DrawPolygon( &points );
966}
967
968
970{
971#if wxCHECK_VERSION( 3, 3, 0 )
972 return Realize();
973#else
974 wxClientDC dc( this );
975
976 if( !dc.IsOk() )
977 return false;
978
979 // calculate hint sizes for both horizontal and vertical
980 // in the order that leaves toolbar in correct final state
981
982 // however, skip calculating alternate orientations if we don't need them due to window style
983 bool retval = true;
984
985 if( m_orientation == wxHORIZONTAL )
986 {
987 if( !( GetWindowStyle() & wxAUI_TB_HORIZONTAL ) )
988 {
989 m_vertHintSize = GetSize();
990 retval = RealizeHelper( dc, false );
991 }
992
993 if( retval && RealizeHelper( dc, true ) )
994 {
995 m_horzHintSize = GetSize();
996 }
997 else
998 {
999 retval = false;
1000 }
1001 }
1002 else
1003 {
1004 if( !( GetWindowStyle() & wxAUI_TB_VERTICAL ) )
1005 {
1006 m_horzHintSize = GetSize();
1007 retval = RealizeHelper( dc, true );
1008 }
1009
1010 if( retval && RealizeHelper( dc, false ) )
1011 {
1012 m_vertHintSize = GetSize();
1013 }
1014 else
1015 {
1016 retval = false;
1017 }
1018 }
1019
1020 Refresh( false );
1021 return retval;
1022#endif
1023}
1024
1025
1026void ACTION_TOOLBAR::onThemeChanged( wxSysColourChangedEvent &aEvent )
1027{
1030
1031 aEvent.Skip();
1032}
1033
1034
1036{
1037 for( const std::pair<int, const TOOL_ACTION*> pair : m_toolActions )
1038 {
1039 wxAuiToolBarItem* tool = FindTool( pair.first );
1040
1041 tool->SetBitmap(
1042 KiBitmapBundle( pair.second->GetIcon(),
1043 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ) );
1044 tool->SetDisabledBitmap( KiDisabledBitmapBundle( pair.second->GetIcon() ) );
1045 }
1046
1047 Refresh();
1048}
1049
1050/*
1051 * Common controls for the toolbar
1052 */
1053ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::gridSelect( "control.GridSelector", _( "Grid Selector" ),
1054 _( "Grid Selection box" ) );
1055
1056ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::zoomSelect( "control.ZoomSelector", _( "Zoom Selector" ),
1057 _( "Zoom Selection box" ) );
1058
1059ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::ipcScripting( "control.IPCPlugin", _( "IPC/Scripting plugins" ),
1060 _( "Region to hold the IPC/Scripting action buttons" ) );
1061
1062ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::layerSelector( "control.LayerSelector", _( "Layer selector" ),
1063 _( "Control to select the layer" ) );
1064
1065ACTION_TOOLBAR_CONTROL ACTION_TOOLBAR_CONTROLS::unitSelector( "control.UnitSelector", _( "Symbol unit selector" ),
1066 _( "Displays the current unit" ) );
#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.
std::string m_name
The default action to display on the toolbar item.
const ACTION_CONDITIONS * GetCondition(const TOOL_ACTION &aAction) const
Get the conditions to use for a specific tool action.
TOOL_ACTION * FindAction(const std::string &aActionName) const
Find an action with a given name (if there is one available).
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:49
void UpdateAll()
Run update handlers for the menu and its submenus.
static ACTION_TOOLBAR_CONTROL gridSelect
static ACTION_TOOLBAR_CONTROL layerSelector
static ACTION_TOOLBAR_CONTROL zoomSelect
static ACTION_TOOLBAR_CONTROL unitSelector
static ACTION_TOOLBAR_CONTROL ipcScripting
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.
void SetButtonSize(wxRect &aSize)
Set the size all the buttons on this palette should be.
void SetGroup(ACTION_GROUP *aGroup)
Set the action group that this palette contains the actions for.
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.
std::map< int, BITMAP_BUTTON * > m_buttons
ACTION_GROUP * GetGroup()
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 onToolRightClick(wxAuiToolBarEvent &aEvent)
Handle the button select inside the palette.
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.
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.
Definition: bitmap_button.h:42
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:565
std::vector< TOOLBAR_ITEM > GetToolbarItems() const
std::vector< TOOLBAR_ITEM > m_GroupItems
std::string m_ActionName
TOOLBAR_ITEM_TYPE m_Type
virtual void RegisterUIUpdateHandler(const TOOL_ACTION &aAction, const ACTION_CONDITIONS &aConditions)
Register an action's update conditions with the UI layer to allow the UI to appropriately display the...
virtual void UnregisterUIUpdateHandler(const TOOL_ACTION &aAction)
Unregister a UI handler for an action that was registered using RegisterUIUpdateHandler.
virtual void RefreshCanvas()
Definition: tools_holder.h:166
Represent a single user action.
Definition: tool_action.h:304
static int GetBaseUIId()
Get the base value used to offset the user interface IDs for the actions.
Definition: tool_action.h:379
BITMAPS GetIcon() const
Return an icon associated with the action.
Definition: tool_action.h:459
const std::string & GetName() const
Return name of the action.
Definition: tool_action.h:337
bool CheckToolbarState(TOOLBAR_STATE aState) const
Check if a specific toolbar state is required for this action.
Definition: tool_action.h:473
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.
Definition: tool_action.h:374
void SetHasPosition(bool aHasPosition)
Definition: tool_event.h:258
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
void CancelTool()
Send a cancel event to the tool currently at the top of the tool stack.
TOOLS_HOLDER * GetToolHolder() const
Definition: tool_manager.h:406
ACTION_MANAGER * GetActionManager() const
Definition: tool_manager.h:306
#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:677
double GetContentScaleFactor(const wxWindow *aWindow)
Tries to determine the content scaling factor currently in use for the window.
Definition: wxgtk/ui.cpp:245
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:902
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 ...
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition: tool_event.h:633
wxLogTrace helper definitions.