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