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