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 <tool/action_toolbar.h>
37#include <tool/actions.h>
38#include <tool/tool_action.h>
39#include <tool/tool_event.h>
41#include <tool/tool_manager.h>
44#include <wx/popupwin.h>
45#include <wx/renderer.h>
46#include <wx/sizer.h>
47#include <wx/dcclient.h>
48#include <wx/settings.h>
49
50
51ACTION_GROUP::ACTION_GROUP( const std::string& aName,
52 const std::vector<const TOOL_ACTION*>& aActions )
53{
54 wxASSERT_MSG( aActions.size() > 0, wxS( "Action groups must have at least one action" ) );
55
56 // The default action is just the first action in the vector
57 m_actions = aActions;
59
60 m_name = aName;
62}
63
64
66{
68}
69
70
72{
73 bool valid = std::any_of( m_actions.begin(), m_actions.end(),
74 [&]( const TOOL_ACTION* aAction ) -> bool
75 {
76 // For some reason, we can't compare the actions directly
77 return aAction->GetId() == aDefault.GetId();
78 } );
79
80 wxASSERT_MSG( valid, wxS( "Action must be present in a group to be the default" ) );
81
82 m_defaultAction = &aDefault;
83}
84
85
86#define PALETTE_BORDER 4 // The border around the palette buttons on all sides
87#define BUTTON_BORDER 1 // The border on the sides of the buttons that touch other buttons
88
89
90ACTION_TOOLBAR_PALETTE::ACTION_TOOLBAR_PALETTE( wxWindow* aParent, bool aVertical ) :
91 wxPopupTransientWindow( aParent, wxBORDER_NONE ),
92 m_group( nullptr ),
93 m_isVertical( aVertical ),
94 m_panel( nullptr ),
95 m_mainSizer( nullptr ),
96 m_buttonSizer( nullptr )
97{
98 m_panel = new wxPanel( this, wxID_ANY );
99 m_panel->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
100
101 // This sizer holds the buttons for the actions
102 m_buttonSizer = new wxBoxSizer( aVertical ? wxVERTICAL : wxHORIZONTAL );
103
104 // This sizer holds the other sizer, so that a consistent border is present on all sides
105 m_mainSizer = new wxBoxSizer( aVertical ? wxVERTICAL : wxHORIZONTAL );
106 m_mainSizer->Add( m_buttonSizer, wxSizerFlags().Border( wxALL, PALETTE_BORDER ) );
107
108 m_panel->SetSizer( m_mainSizer );
109
110 Connect( wxEVT_CHAR_HOOK, wxCharEventHandler( ACTION_TOOLBAR_PALETTE::onCharHook ),
111 nullptr, this );
112}
113
114
116{
118 wxBitmapBundle normalBmp = KiBitmapBundle( aAction.GetIcon(), size );
119
120 int bmpWidth = normalBmp.GetPreferredBitmapSizeFor( this ).GetWidth();
121 int padding = ( m_buttonSize.GetWidth() - bmpWidth ) / 2;
122 wxSize bmSize( size, size );
123 bmSize *= KIPLATFORM::UI::GetContentScaleFactor( m_parent );
124
125 BITMAP_BUTTON* button = new BITMAP_BUTTON( m_panel, aAction.GetUIId() );
126
127 button->SetIsToolbarButton();
128 button->SetBitmap( normalBmp );
129 button->SetDisabledBitmap( KiDisabledBitmapBundle( aAction.GetIcon() ) );
130 button->SetPadding( padding );
131 button->SetToolTip( aAction.GetButtonTooltip() );
132 button->AcceptDragInAsClick();
133 button->SetBitmapCentered();
134
135 m_buttons[aAction.GetUIId()] = button;
136
137 if( m_isVertical )
138 m_buttonSizer->Add( button, wxSizerFlags().Border( wxTOP | wxBOTTOM, BUTTON_BORDER ) );
139 else
140 m_buttonSizer->Add( button, wxSizerFlags().Border( wxLEFT | wxRIGHT, BUTTON_BORDER ) );
141
142 m_buttonSizer->Layout();
143}
144
145
146void ACTION_TOOLBAR_PALETTE::EnableAction( const TOOL_ACTION& aAction, bool aEnable )
147{
148 auto it = m_buttons.find( aAction.GetUIId() );
149
150 if( it != m_buttons.end() )
151 it->second->Enable( aEnable );
152}
153
154
155void ACTION_TOOLBAR_PALETTE::CheckAction( const TOOL_ACTION& aAction, bool aCheck )
156{
157 auto it = m_buttons.find( aAction.GetUIId() );
158
159 if( it != m_buttons.end() )
160 it->second->Check( aCheck );
161}
162
163
164void ACTION_TOOLBAR_PALETTE::Popup( wxWindow* aFocus )
165{
166 m_mainSizer->Fit( m_panel );
167 SetClientSize( m_panel->GetSize() );
168
169 wxPopupTransientWindow::Popup( aFocus );
170}
171
172
173void ACTION_TOOLBAR_PALETTE::onCharHook( wxKeyEvent& aEvent )
174{
175 // Allow the escape key to dismiss this popup
176 if( aEvent.GetKeyCode() == WXK_ESCAPE )
177 Dismiss();
178 else
179 aEvent.Skip();
180}
181
182
183ACTION_TOOLBAR::ACTION_TOOLBAR( EDA_BASE_FRAME* parent, wxWindowID id, const wxPoint& pos,
184 const wxSize& size, long style ) :
185 wxAuiToolBar( parent, id, pos, size, style ),
186 m_paletteTimer( nullptr ),
187 m_auiManager( nullptr ),
188 m_toolManager( parent->GetToolManager() ),
189 m_palette( nullptr )
190{
191 m_paletteTimer = new wxTimer( this );
192
193 SetArtProvider( new WX_AUI_TOOLBAR_ART );
194
195 Connect( wxEVT_COMMAND_TOOL_CLICKED, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolEvent ),
196 nullptr, this );
197 Connect( wxEVT_AUITOOLBAR_RIGHT_CLICK,
198 wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolRightClick ),
199 nullptr, this );
200 Connect( wxEVT_AUITOOLBAR_BEGIN_DRAG, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onItemDrag ),
201 nullptr, this );
202 Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
203 Connect( wxEVT_LEFT_UP, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
204 Connect( m_paletteTimer->GetId(), wxEVT_TIMER,
205 wxTimerEventHandler( ACTION_TOOLBAR::onTimerDone ), nullptr, this );
206
207 Bind( wxEVT_SYS_COLOUR_CHANGED,
208 wxSysColourChangedEventHandler( ACTION_TOOLBAR::onThemeChanged ), this );
209}
210
211
213{
214 Disconnect( wxEVT_COMMAND_TOOL_CLICKED, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolEvent ),
215 nullptr, this );
216 Disconnect( wxEVT_AUITOOLBAR_RIGHT_CLICK,
217 wxAuiToolBarEventHandler( ACTION_TOOLBAR::onToolRightClick ), nullptr, this );
218 Disconnect( wxEVT_AUITOOLBAR_BEGIN_DRAG, wxAuiToolBarEventHandler( ACTION_TOOLBAR::onItemDrag ),
219 nullptr, this );
220 Disconnect( wxEVT_LEFT_DOWN, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr,
221 this );
222 Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( ACTION_TOOLBAR::onMouseClick ), nullptr, this );
223 Disconnect( m_paletteTimer->GetId(), wxEVT_TIMER,
224 wxTimerEventHandler( ACTION_TOOLBAR::onTimerDone ), nullptr, this );
225
226 Unbind( wxEVT_SYS_COLOUR_CHANGED,
227 wxSysColourChangedEventHandler( ACTION_TOOLBAR::onThemeChanged ), this );
228
229 delete m_paletteTimer;
230
231 // Clear all the maps keeping track of our items on the toolbar
232 m_toolMenus.clear();
233 m_actionGroups.clear();
234 m_toolCancellable.clear();
235 m_toolKinds.clear();
236 m_toolActions.clear();
237}
238
239
240void ACTION_TOOLBAR::Add( const TOOL_ACTION& aAction, bool aIsToggleEntry, bool aIsCancellable )
241{
242 wxASSERT( GetParent() );
243 wxASSERT_MSG( !( aIsCancellable && !aIsToggleEntry ),
244 wxS( "aIsCancellable requires aIsToggleEntry" ) );
245
246 int toolId = aAction.GetUIId();
247
248 AddTool( toolId, wxEmptyString,
249 KiBitmapBundle( aAction.GetIcon(),
250 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ),
251 KiDisabledBitmapBundle( aAction.GetIcon() ),
252 aIsToggleEntry ? wxITEM_CHECK : wxITEM_NORMAL,
253 aAction.GetButtonTooltip(), wxEmptyString, nullptr );
254
255 m_toolKinds[ toolId ] = aIsToggleEntry;
256 m_toolActions[ toolId ] = &aAction;
257 m_toolCancellable[ toolId ] = aIsCancellable;
258}
259
260
262{
263 int toolId = aAction.GetUIId();
264
265 AddTool( toolId, wxEmptyString,
266 KiBitmapBundle( aAction.GetIcon(),
267 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ),
268 KiDisabledBitmapBundle( aAction.GetIcon() ), wxITEM_NORMAL,
269 aAction.GetButtonTooltip(), wxEmptyString, nullptr );
270
271 m_toolKinds[ toolId ] = false;
272 m_toolActions[ toolId ] = &aAction;
273}
274
275
276void ACTION_TOOLBAR::AddScaledSeparator( wxWindow* aWindow )
277{
278 int scale = KiIconScale( aWindow );
279
280 if( scale > 4 )
281 AddSpacer( 16 * ( scale - 4 ) / 4 );
282
283 AddSeparator();
284
285 if( scale > 4 )
286 AddSpacer( 16 * ( scale - 4 ) / 4 );
287}
288
289
291 std::unique_ptr<ACTION_MENU> aMenu )
292{
293 int toolId = aAction.GetUIId();
294
295 m_toolMenus[toolId] = std::move( aMenu );
296}
297
298
299void ACTION_TOOLBAR::AddGroup( ACTION_GROUP* aGroup, bool aIsToggleEntry )
300{
301 int groupId = aGroup->GetUIId();
302 const TOOL_ACTION* defaultAction = aGroup->GetDefaultAction();
303
304 wxASSERT( GetParent() );
305 wxASSERT( defaultAction );
306
307 m_toolKinds[ groupId ] = aIsToggleEntry;
308 m_toolActions[ groupId ] = defaultAction;
309 m_actionGroups[ groupId ] = aGroup;
310
311 // Add the main toolbar item representing the group
312 AddTool( groupId, wxEmptyString,
313 KiBitmapBundle( defaultAction->GetIcon(),
314 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ),
315 KiDisabledBitmapBundle( defaultAction->GetIcon() ),
316 aIsToggleEntry ? wxITEM_CHECK : wxITEM_NORMAL,
317 wxEmptyString, wxEmptyString, nullptr );
318
319 // Select the default action
320 doSelectAction( aGroup, *defaultAction );
321}
322
323
325{
326 bool valid = std::any_of( aGroup->m_actions.begin(), aGroup->m_actions.end(),
327 [&]( const TOOL_ACTION* action2 ) -> bool
328 {
329 // For some reason, we can't compare the actions directly
330 return aAction.GetId() == action2->GetId();
331 } );
332
333 if( valid )
334 doSelectAction( aGroup, aAction );
335}
336
337
339{
340 wxASSERT( GetParent() );
341
342 int groupId = aGroup->GetUIId();
343
344 wxAuiToolBarItem* item = FindTool( groupId );
345
346 if( !item )
347 return;
348
349 // Update the item information
350 item->SetShortHelp( aAction.GetButtonTooltip() );
351 item->SetBitmap( KiBitmapBundle( aAction.GetIcon(),
352 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ) );
353 item->SetDisabledBitmap( KiDisabledBitmapBundle( aAction.GetIcon() ) );
354
355 // Register a new handler with the new UI conditions
356 if( m_toolManager )
357 {
359
360 wxASSERT_MSG( cond, wxString::Format( "Missing UI condition for action %s",
361 aAction.GetName() ) );
362
365 }
366
367 // Update the currently selected action
368 m_toolActions[ groupId ] = &aAction;
369
370 Refresh();
371}
372
373
375{
376 wxAuiToolBarItem* item = FindTool( aID );
377 wxASSERT_MSG( item, wxString::Format( "No toolbar item found for ID %d", aID ) );
378
379 // The control on the toolbar is stored inside the window field of the item
380 wxControl* control = dynamic_cast<wxControl*>( item->GetWindow() );
381 wxASSERT_MSG( control,
382 wxString::Format( "No control located in toolbar item with ID %d", aID ) );
383
384 // Update the size the item has stored using the best size of the control
385 control->InvalidateBestSize();
386 wxSize bestSize = control->GetBestSize();
387 item->SetMinSize( bestSize );
388
389 // Update the sizer item sizes
390 // This is a bit convoluted because there are actually 2 sizers that need to be updated:
391 // 1. The main sizer that is used for the entire toolbar (this sizer item can be found in the
392 // toolbar item)
393 if( wxSizerItem* szrItem = item->GetSizerItem() )
394 szrItem->SetMinSize( bestSize );
395
396 // 2. The controls have a second sizer that allows for padding above/below the control with
397 // stretch space, so we also need to update the sizer item for the control in that sizer with
398 // the new size. We let wx do the search for us, since SetItemMinSize is recursive and will
399 // locate the control on that sizer.
400 if( m_sizer )
401 {
402 m_sizer->SetItemMinSize( control, bestSize );
403
404 // Now actually update the toolbar with the new sizes
405 m_sizer->Layout();
406 }
407}
408
409
411{
412 // Clear all the maps keeping track of our items on the toolbar
413 m_toolMenus.clear();
414 m_actionGroups.clear();
415 m_toolCancellable.clear();
416 m_toolKinds.clear();
417 m_toolActions.clear();
418
419 // Remove the actual tools from the toolbar
420 Clear();
421}
422
423
424void ACTION_TOOLBAR::SetToolBitmap( const TOOL_ACTION& aAction, const wxBitmap& aBitmap )
425{
426 int toolId = aAction.GetUIId();
427 wxAuiToolBar::SetToolBitmap( toolId, aBitmap );
428
429 // Set the disabled bitmap: we use the disabled bitmap version of aBitmap.
430 wxAuiToolBarItem* tb_item = wxAuiToolBar::FindTool( toolId );
431
432 if( tb_item )
433 {
434 tb_item->SetDisabledBitmap(
435 aBitmap.ConvertToDisabled( KIPLATFORM::UI::IsDarkTheme() ? 70 : 255 ) );
436 }
437}
438
439
440void ACTION_TOOLBAR::Toggle( const TOOL_ACTION& aAction, bool aState )
441{
442 int toolId = aAction.GetUIId();
443
444 if( m_toolKinds[ toolId ] )
445 ToggleTool( toolId, aState );
446 else
447 EnableTool( toolId, aState );
448}
449
450
451void ACTION_TOOLBAR::Toggle( const TOOL_ACTION& aAction, bool aEnabled, bool aChecked )
452{
453 int toolId = aAction.GetUIId();
454
455 EnableTool( toolId, aEnabled );
456 ToggleTool( toolId, aEnabled && aChecked );
457}
458
459
460void ACTION_TOOLBAR::onToolEvent( wxAuiToolBarEvent& aEvent )
461{
462 int id = aEvent.GetId();
463 wxEventType type = aEvent.GetEventType();
464 OPT_TOOL_EVENT evt;
465
466 bool handled = false;
467
468 if( m_toolManager && type == wxEVT_COMMAND_TOOL_CLICKED && id >= TOOL_ACTION::GetBaseUIId() )
469 {
470 const auto actionIt = m_toolActions.find( id );
471
472 // The toolbar item is toggled before the event is sent, so we check for it not being
473 // toggled to see if it was toggled originally
474 if( m_toolCancellable[id] && !GetToolToggled( id ) )
475 {
476 // Send a cancel event
478 handled = true;
479 }
480 else if( actionIt != m_toolActions.end() )
481 {
482 // Dispatch a tool event
483 evt = actionIt->second->MakeEvent();
484 evt->SetHasPosition( false );
487 handled = true;
488 }
489 }
490
491 // Skip the event if we don't handle it
492 if( !handled )
493 aEvent.Skip();
494}
495
496
497void ACTION_TOOLBAR::onToolRightClick( wxAuiToolBarEvent& aEvent )
498{
499 int toolId = aEvent.GetToolId();
500
501 // This means the event was not on a button
502 if( toolId == -1 )
503 return;
504
505 // Ensure that the ID used maps to a proper tool ID.
506 // If right-clicked on a group item, this is needed to get the ID of the currently selected
507 // action, since the event's ID is that of the group.
508 const auto actionIt = m_toolActions.find( toolId );
509
510 if( actionIt != m_toolActions.end() )
511 toolId = actionIt->second->GetUIId();
512
513 // Find the menu for the action
514 const auto menuIt = m_toolMenus.find( toolId );
515
516 if( menuIt == m_toolMenus.end() )
517 return;
518
519 // Update and show the menu
520 std::unique_ptr<ACTION_MENU>& owningMenu = menuIt->second;
521
522 // Get the actual menu pointer to show it
523 ACTION_MENU* menu = owningMenu.get();
524 SELECTION dummySel;
525
526 if( CONDITIONAL_MENU* condMenu = dynamic_cast<CONDITIONAL_MENU*>( menu ) )
527 condMenu->Evaluate( dummySel );
528
529 menu->UpdateAll();
530 PopupMenu( menu );
531
532 // Remove hovered item when the menu closes, otherwise it remains hovered even if the
533 // mouse is not on the toolbar
534 SetHoverItem( nullptr );
535}
536
537
538// The time (in milliseconds) between pressing the left mouse button and opening the palette
539#define PALETTE_OPEN_DELAY 500
540
541
542void ACTION_TOOLBAR::onMouseClick( wxMouseEvent& aEvent )
543{
544 wxAuiToolBarItem* item = FindToolByPosition( aEvent.GetX(), aEvent.GetY() );
545
546 if( item )
547 {
548 // Ensure there is no active palette
549 if( m_palette )
550 {
551 m_palette->Hide();
552 m_palette->Destroy();
553 m_palette = nullptr;
554 }
555
556 // Start the popup conditions if it is a left mouse click and the tool clicked is a group
557 if( aEvent.LeftDown() && ( m_actionGroups.find( item->GetId() ) != m_actionGroups.end() ) )
559
560 // Clear the popup conditions if it is a left up, because that implies a click happened
561 if( aEvent.LeftUp() )
562 m_paletteTimer->Stop();
563 }
564
565 // Skip the event so wx can continue processing the mouse event
566 aEvent.Skip();
567}
568
569
570void ACTION_TOOLBAR::onItemDrag( wxAuiToolBarEvent& aEvent )
571{
572 int toolId = aEvent.GetToolId();
573
574 if( m_actionGroups.find( toolId ) != m_actionGroups.end() )
575 {
576 wxAuiToolBarItem* item = FindTool( toolId );
577
578 // Use call after because opening the palette from a mouse handler
579 // creates a weird mouse state that causes problems on OSX.
580 CallAfter( &ACTION_TOOLBAR::popupPalette, item );
581
582 // Don't skip this event since we are handling it
583 return;
584 }
585
586 // Skip since we don't care about it
587 aEvent.Skip();
588}
589
590
591void ACTION_TOOLBAR::onTimerDone( wxTimerEvent& aEvent )
592{
593 // We need to search for the tool using the client coordinates
594 wxPoint mousePos = ScreenToClient( KIPLATFORM::UI::GetMousePosition() );
595
596 wxAuiToolBarItem* item = FindToolByPosition( mousePos.x, mousePos.y );
597
598 if( item )
599 popupPalette( item );
600}
601
602
603void ACTION_TOOLBAR::onPaletteEvent( wxCommandEvent& aEvent )
604{
605 if( !m_palette )
606 return;
607
608 OPT_TOOL_EVENT evt;
610
611 // Find the action corresponding to the button press
612 auto actionIt = std::find_if( group->GetActions().begin(), group->GetActions().end(),
613 [&]( const TOOL_ACTION* aAction )
614 {
615 return aAction->GetUIId() == aEvent.GetId();
616 } );
617
618 if( actionIt != group->GetActions().end() )
619 {
620 const TOOL_ACTION* action = *actionIt;
621
622 // Dispatch a tool event
623 evt = action->MakeEvent();
624 evt->SetHasPosition( false );
627
628 // Update the main toolbar item with the selected action
629 doSelectAction( group, *action );
630 }
631
632 // Hide the palette
633 m_palette->Hide();
634 m_palette->Destroy();
635 m_palette = nullptr;
636}
637
638
639void ACTION_TOOLBAR::popupPalette( wxAuiToolBarItem* aItem )
640{
641 // Clear all popup conditions
642 m_paletteTimer->Stop();
643
644 wxWindow* toolParent = dynamic_cast<wxWindow*>( m_toolManager->GetToolHolder() );
645
646 wxASSERT( GetParent() );
647 wxASSERT( m_auiManager );
648 wxASSERT( toolParent );
649
650 // Ensure the item we are using for the palette has a group associated with it.
651 const auto it = m_actionGroups.find( aItem->GetId() );
652
653 if( it == m_actionGroups.end() )
654 return;
655
656 ACTION_GROUP* group = it->second;
657
658 wxAuiPaneInfo& pane = m_auiManager->GetPane( this );
659
660 // We use the size of the toolbar items for our palette buttons
661 wxRect toolRect = GetToolRect( aItem->GetId() );
662
663 // The position for the palette window must be in screen coordinates
664 wxPoint pos( ClientToScreen( toolRect.GetPosition() ) );
665
666 // True for vertical buttons, false for horizontal
667 bool dir = true;
668 size_t numActions = group->m_actions.size();
669
670 // The size of the palette in the long dimension
671 int paletteLongDim = ( 2 * PALETTE_BORDER ) // The border on all sides of the buttons
672 + ( BUTTON_BORDER ) // The border on the start of the buttons
673 + ( numActions * BUTTON_BORDER ) // The other button borders
674 + ( numActions * toolRect.GetHeight() ); // The size of the buttons
675
676 // Determine the position of the top left corner of the palette window
677 switch( pane.dock_direction )
678 {
679 case wxAUI_DOCK_TOP:
680 // Top toolbars need to shift the palette window down by the toolbar padding
681 dir = true; // Buttons are vertical in the palette
682 pos = ClientToScreen( toolRect.GetBottomLeft() );
683 pos += wxPoint( -PALETTE_BORDER, // Shift left to align the button edges
684 m_bottomPadding ); // Shift down to move away from the toolbar
685 break;
686
687 case wxAUI_DOCK_BOTTOM:
688 // Bottom toolbars need to shift the palette window up by its height (all buttons +
689 // border + toolbar padding)
690 dir = true; // Buttons are vertical in the palette
691 pos = ClientToScreen( toolRect.GetTopLeft() );
692 pos += wxPoint( -PALETTE_BORDER, // Shift left to align the button
693 // Shift up by the entire length of the palette.
694 -( paletteLongDim + m_topPadding ) );
695 break;
696
697 case wxAUI_DOCK_LEFT:
698 // Left toolbars need to shift the palette window up by the toolbar padding
699 dir = false; // Buttons are horizontal in the palette
700 pos = ClientToScreen( toolRect.GetTopRight() );
701 pos += wxPoint( m_rightPadding, // Shift right to move away from the toolbar
702 -( PALETTE_BORDER ) ); // Shift up to align the button tops
703 break;
704
705 case wxAUI_DOCK_RIGHT:
706 // Right toolbars need to shift the palette window left by its width (all buttons +
707 // border + toolbar padding)
708 dir = false; // Buttons are horizontal in the palette
709 pos = ClientToScreen( toolRect.GetTopLeft() );
710
711 // Shift left by the entire length of the palette.
712 pos += wxPoint( -( paletteLongDim + m_leftPadding ),
713 -( PALETTE_BORDER ) ); // Shift up to align the button
714 break;
715 }
716
717 m_palette = new ACTION_TOOLBAR_PALETTE( GetParent(), dir );
718
719 // We handle the button events in the toolbar class, so connect the right handler
721 m_palette->SetButtonSize( toolRect );
722 m_palette->Connect( wxEVT_BUTTON, wxCommandEventHandler( ACTION_TOOLBAR::onPaletteEvent ),
723 nullptr, this );
724
725
726 // Add the actions in the group to the palette and update their enabled state
727 // We purposely don't check items in the palette
728 for( const TOOL_ACTION* action : group->m_actions )
729 {
730 wxUpdateUIEvent evt( action->GetUIId() );
731
732 toolParent->ProcessWindowEvent( evt );
733
734 m_palette->AddAction( *action );
735
736 if( evt.GetSetEnabled() )
737 m_palette->EnableAction( *action, evt.GetEnabled() );
738 }
739
740 // Release the mouse to ensure the first click will be recognized in the palette
741 ReleaseMouse();
742
743 m_palette->SetPosition( pos );
744 m_palette->Popup();
745
746 // Clear the mouse state on the toolbar because otherwise wxWidgets gets confused
747 // and won't properly display any highlighted items after the palette is closed.
748 // (This is the equivalent of calling the DoResetMouseState() private function)
749 RefreshOverflowState();
750 SetHoverItem( nullptr );
751 SetPressedItem( nullptr );
752
753 m_dragging = false;
754 m_tipItem = nullptr;
755 m_actionPos = wxPoint( -1, -1 );
756 m_actionItem = nullptr;
757}
758
759
760void ACTION_TOOLBAR::OnCustomRender(wxDC& aDc, const wxAuiToolBarItem& aItem, const wxRect& aRect )
761{
762 auto it = m_actionGroups.find( aItem.GetId() );
763
764 if( it == m_actionGroups.end() )
765 return;
766
767 // Choose the color to draw the triangle
768 wxColour clr;
769
770 if( aItem.GetState() & wxAUI_BUTTON_STATE_DISABLED )
771 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT );
772 else
773 clr = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNTEXT );
774
775 // Must set both the pen (for the outline) and the brush (for the polygon fill)
776 aDc.SetPen( wxPen( clr ) );
777 aDc.SetBrush( wxBrush( clr ) );
778
779 // Make the side length of the triangle approximately 1/5th of the bitmap
780 int sideLength = KiROUND( aRect.height / 5.0 );
781
782 // This will create a triangle with its point at the bottom right corner,
783 // and its other two corners along the right and bottom sides
784 wxPoint btmRight = aRect.GetBottomRight();
785 wxPoint topCorner( btmRight.x, btmRight.y - sideLength );
786 wxPoint btmCorner( btmRight.x - sideLength, btmRight.y );
787
788 wxPointList points;
789 points.Append( &btmRight );
790 points.Append( &topCorner );
791 points.Append( &btmCorner );
792
793 aDc.DrawPolygon( &points );
794}
795
796
798{
799#if wxCHECK_VERSION( 3, 3, 0 )
800 return Realize();
801#else
802 wxClientDC dc( this );
803
804 if( !dc.IsOk() )
805 return false;
806
807 // calculate hint sizes for both horizontal and vertical
808 // in the order that leaves toolbar in correct final state
809
810 // however, skip calculating alternate orientations if we don't need them due to window style
811 bool retval = true;
812
813 if( m_orientation == wxHORIZONTAL )
814 {
815 if( !( GetWindowStyle() & wxAUI_TB_HORIZONTAL ) )
816 {
817 m_vertHintSize = GetSize();
818 retval = RealizeHelper( dc, false );
819 }
820
821 if( retval && RealizeHelper( dc, true ) )
822 {
823 m_horzHintSize = GetSize();
824 }
825 else
826 {
827 retval = false;
828 }
829 }
830 else
831 {
832 if( !( GetWindowStyle() & wxAUI_TB_VERTICAL ) )
833 {
834 m_horzHintSize = GetSize();
835 retval = RealizeHelper( dc, true );
836 }
837
838 if( retval && RealizeHelper( dc, false ) )
839 {
840 m_vertHintSize = GetSize();
841 }
842 else
843 {
844 retval = false;
845 }
846 }
847
848 Refresh( false );
849 return retval;
850#endif
851}
852
853
854void ACTION_TOOLBAR::onThemeChanged( wxSysColourChangedEvent &aEvent )
855{
858
859 aEvent.Skip();
860}
861
862
864{
865 for( const std::pair<int, const TOOL_ACTION*> pair : m_toolActions )
866 {
867 wxAuiToolBarItem* tool = FindTool( pair.first );
868
869 tool->SetBitmap(
870 KiBitmapBundle( pair.second->GetIcon(),
871 Pgm().GetCommonSettings()->m_Appearance.toolbar_icon_size ) );
872 tool->SetDisabledBitmap( KiDisabledBitmapBundle( pair.second->GetIcon() ) );
873 }
874
875 Refresh();
876}
#define PALETTE_BORDER
#define PALETTE_OPEN_DELAY
#define BUTTON_BORDER
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.
std::vector< const TOOL_ACTION * > m_actions
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.
const TOOL_ACTION * GetDefaultAction() const
Get the default action to use when first creating this group's toolbar palette icon.
std::string m_name
The default action to display on the toolbar item.
ACTION_GROUP(const std::string &aName, const std::vector< const TOOL_ACTION * > &aActions)
const ACTION_CONDITIONS * GetCondition(const TOOL_ACTION &aAction) const
Get the conditions to use for a specific tool action.
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.
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 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
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
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.
std::map< int, ACTION_GROUP * > m_actionGroups
void AddGroup(ACTION_GROUP *aGroup, bool aIsToggleEntry=false)
Add a set of actions to a toolbar as a group.
void Add(const TOOL_ACTION &aAction, bool aIsToggleEntry=false, bool aIsCancellable=false)
Add a TOOL_ACTION-based button to the toolbar.
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.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition: pgm_base.cpp:689
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:269
static int GetBaseUIId()
Get the base value used to offset the user interface IDs for the actions.
Definition: tool_action.h:344
BITMAPS GetIcon() const
Return an icon associated with the action.
Definition: tool_action.h:424
const std::string & GetName() const
Return name of the action.
Definition: tool_action.h:302
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:339
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:402
ACTION_MANAGER * GetActionManager() const
Definition: tool_manager.h:302
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:1073
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