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