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>
40 #include <tool/tool_interactive.h>
41 #include <tool/tool_manager.h>
42 #include <widgets/bitmap_button.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 
51 wxBitmap MakeDisabledBitmap( const wxBitmap& aSource )
52 {
53  return aSource.ConvertToDisabled( KIPLATFORM::UI::IsDarkTheme() ? 70 : 255 );
54 }
55 
56 
57 ACTION_GROUP::ACTION_GROUP( const std::string& aName,
58  const std::vector<const TOOL_ACTION*>& aActions )
59 {
60  wxASSERT_MSG( aActions.size() > 0, wxT( "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 {
73  return m_id + TOOL_ACTION::GetBaseUIId();
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, wxT( "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 
96 ACTION_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 
147 void 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 
156 void 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 
165 void 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 
174 void 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 
184 ACTION_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
196  WX_AUI_TOOLBAR_ART* newArt = new WX_AUI_TOOLBAR_ART();
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 
230 void ACTION_TOOLBAR::Add( const TOOL_ACTION& aAction, bool aIsToggleEntry, bool aIsCancellable )
231 {
232  wxASSERT( GetParent() );
233  wxASSERT_MSG( !( aIsCancellable && !aIsToggleEntry ), wxT( "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 
261 void 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 
287 void 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  {
348  const ACTION_CONDITIONS* cond = m_toolManager->GetActionManager()->GetCondition( aAction );
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 
412 void 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 
425 void 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 
436 void 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 
445 void 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 );
470  m_toolManager->ProcessEvent( *evt );
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 
482 void 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 
526 void 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() ) )
542  m_paletteTimer->StartOnce( PALETTE_OPEN_DELAY );
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 
554 void 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 
575 void 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 
587 void 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 );
609  m_toolManager->ProcessEvent( *evt );
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 
623 void 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 
744 void 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 
834 void ACTION_TOOLBAR::onThemeChanged( wxSysColourChangedEvent &aEvent )
835 {
837  RefreshBitmaps();
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 }
wxBitmap MakeDisabledBitmap(const wxBitmap &aSource)
const TOOL_ACTION * m_defaultAction
The actions that compose the group.
A bitmap button widget that behaves like an AUI toolbar item's button when it is drawn.
Definition: bitmap_button.h:39
void onItemDrag(wxAuiToolBarEvent &aEvent)
The default tool event handler.
void CheckAction(const TOOL_ACTION &aAction, bool aCheck=true)
Check/Toggle the button for an action on the palette.
static int MakeActionId(const std::string &aActionName)
Generate an unique ID from for an action with given name.
int GetUIId() const
Definition: tool_action.h:130
ACTION_GROUP * GetGroup()
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:48
void SetButtonSize(wxRect &aSize)
Set the size all the buttons on this palette should be.
void AddToolContextMenu(const TOOL_ACTION &aAction, std::unique_ptr< ACTION_MENU > aMenu)
Add a context menu to a specific tool item on the toolbar.
virtual void RefreshCanvas()
Definition: tools_holder.h:153
void AddScaledSeparator(wxWindow *aWindow)
Add a separator that introduces space on either side to not squash the tools when scaled.
void popupPalette(wxAuiToolBarItem *aItem)
Popup the ACTION_TOOLBAR_PALETTE associated with the ACTION_GROUP of the given toolbar item.
void Add(const TOOL_ACTION &aAction, bool aIsToggleEntry=false, bool aIsCancellable=false)
Add a TOOL_ACTION-based button to the toolbar.
int KiIconScale(wxWindow *aWindow)
Return the automatic scale factor that would be used for a given window by KiScaledBitmap and KiScale...
Definition: bitmap.cpp:122
#define PALETTE_OPEN_DELAY
void onToolRightClick(wxAuiToolBarEvent &aEvent)
Handle the button select inside the palette.
virtual void UnregisterUIUpdateHandler(const TOOL_ACTION &aAction)
Unregister a UI handler for an action that was registered using RegisterUIUpdateHandler.
Definition: tools_holder.h:83
void onTimerDone(wxTimerEvent &aEvent)
bool IsDarkTheme()
Determine if the desktop interface is currently using a dark theme or a light theme.
Definition: gtk/ui.cpp:31
void AddGroup(ACTION_GROUP *aGroup, bool aIsToggleEntry=false)
Add a set of actions to a toolbar as a group.
void UpdateAll()
Run update handlers for the menu and its submenus.
ACTION_TOOLBAR_PALETTE(wxWindow *aParent, bool aVertical)
Create the palette.
void onToolEvent(wxAuiToolBarEvent &aEvent)
Handle a right-click on a menu item.
std::map< int, const TOOL_ACTION * > m_toolActions
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:106
bool KiRealize()
Use this over Realize() to avoid a rendering glitch with fixed orientation toolbars.
static int GetBaseUIId()
Definition: tool_action.h:135
void SetDefaultAction(const TOOL_ACTION &aDefault)
Set the default action to use when first creating the toolbar palette icon.
void onMouseClick(wxMouseEvent &aEvent)
Handler for when a drag event occurs on an item.
A group of actions that will be displayed together on a toolbar palette.
ACTION_GROUP(const std::string &aName, const std::vector< const TOOL_ACTION * > &aActions)
void AddAction(const TOOL_ACTION &aAction)
Add an action to the palette.
void AddButton(const TOOL_ACTION &aAction)
Add a large button such as used in the KiCad Manager Frame's launch bar.
wxWidgets 3.1 has support for dark mode detection, but 3.0 doesn't.
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
void doSelectAction(ACTION_GROUP *aGroup, const TOOL_ACTION &aAction)
Update a group toolbar item to look like a specific action.
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
std::map< int, bool > m_toolCancellable
void UpdateControlWidth(int aID)
Update the toolbar item width of a control using its best size.
void SetGroup(ACTION_GROUP *aGroup)
Set the action group that this palette contains the actions for.
void SetPadding(int aPadding)
Set the amount of padding present on each side of the bitmap.
const BITMAPS GetIcon() const
Return an icon associated with the action.
Definition: tool_action.h:179
void OnCustomRender(wxDC &aDc, const wxAuiToolBarItem &aItem, const wxRect &aRect) override
void onThemeChanged(wxSysColourChangedEvent &aEvent)
Render the triangle in the lower-right corner that represents that an action palette is available for...
void ThemeChanged()
Notifies the store that the icon theme has been changed by the user, so caches must be invalidated.
ACTION_TOOLBAR_PALETTE * m_palette
wxString GetDescription(bool aIncludeHotkey=true) const
Definition: tool_action.cpp:97
std::vector< const TOOL_ACTION * > m_actions
ACTION_TOOLBAR(EDA_BASE_FRAME *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxAUI_TB_DEFAULT_STYLE)
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:148
std::map< int, std::unique_ptr< ACTION_MENU > > m_toolMenus
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
wxRect m_buttonSize
True if the palette uses vertical buttons, false for horizontal buttons.
TOOLS_HOLDER * GetToolHolder() const
Definition: tool_manager.h:294
wxAuiManager * m_auiManager
void RefreshBitmaps()
Reload all the bitmaps for the tools (e.g.
const TOOL_ACTION * GetDefaultAction() const
Get the default action to use when first creating this group's toolbar palette icon.
TOOL_MANAGER * m_toolManager
int GetId() const
Return the unique id of the TOOL_ACTION object.
Definition: tool_action.h:121
const int scale
see class PGM_BASE
std::map< int, ACTION_GROUP * > m_actionGroups
void SetBitmap(const wxBitmap &aBmp)
Set the bitmap shown when the button is enabled.
Functors that can be used to figure out how the action controls should be displayed in the UI and if ...
std::map< int, BITMAP_BUTTON * > m_buttons
The base frame for deriving all KiCad main window classes.
Represent a single user action.
Definition: tool_action.h:67
#define PALETTE_BORDER
void SetToolBitmap(const TOOL_ACTION &aAction, const wxBitmap &aBitmap)
Updates the bitmap of a particular tool.
TOOL_EVENT MakeEvent() const
Return the event associated with the action (i.e.
Definition: tool_action.cpp:72
void Toggle(const TOOL_ACTION &aAction, bool aState)
Apply the default toggle action.
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:73
int GetUIId() const
Get the ID used in the UI to reference this group.
wxBoxSizer * m_buttonSizer
The buttons that act as the toolbar on the palette.
virtual ~ACTION_TOOLBAR()
void AcceptDragInAsClick(bool aAcceptDragIn=true)
Accept mouse-up as click even if mouse-down happened outside of the control.
ACTION_MANAGER * GetActionManager() const
Definition: tool_manager.h:199
void SelectAction(ACTION_GROUP *aGroup, const TOOL_ACTION &aAction)
Select an action inside a group.
const ACTION_CONDITIONS * GetCondition(const TOOL_ACTION &aAction) const
Get the conditions to use for a specific tool action.
void SetDisabledBitmap(const wxBitmap &aBmp)
Set the bitmap shown when the button is disabled.
int m_id
< The action ID for this action group
void Popup(wxWindow *aFocus=nullptr) override
Popup this window.
#define BUTTON_BORDER
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
void EnableAction(const TOOL_ACTION &aAction, bool aEnable=true)
Enable the button for an action on the palette.
void onCharHook(wxKeyEvent &aEvent)
std::map< int, bool > m_toolKinds
wxTimer * m_paletteTimer
A popup window that contains a row of toolbar-like buttons for the user to choose from.
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:241
void CancelTool()
Send a cancel event to the tool currently at the top of the tool stack.
void onPaletteEvent(wxCommandEvent &aEvent)
Handle the palette timer triggering.
OPT< TOOL_EVENT > OPT_TOOL_EVENT
Definition: tool_event.h:548
std::string m_name
The default action to display on the toolbar item.
const std::string & GetName() const
Return name of the action.
Definition: tool_action.h:101
void ClearToolbar()
Clear the toolbar and remove all associated menus.
BITMAP_STORE * GetBitmapStore()
Definition: bitmap.cpp:93