1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <o3tl/safeint.hxx>
21 #include <sfx2/sidebar/FocusManager.hxx>
22 #include <sfx2/sidebar/Deck.hxx>
23 #include <sfx2/sidebar/Panel.hxx>
24 #include <sidebar/DeckTitleBar.hxx>
25 #include <sidebar/PanelTitleBar.hxx>
26 #include <sidebar/TitleBar.hxx>
28 #include <vcl/event.hxx>
29 #include <vcl/weld.hxx>
31 namespace sfx2::sidebar
{
33 FocusManager::FocusLocation::FocusLocation (const PanelComponent eComponent
, const sal_Int32 nIndex
)
34 : meComponent(eComponent
),
39 FocusManager::FocusManager(std::function
<void(const Panel
&)> aShowPanelFunctor
)
40 : mpDeckTitleBar(nullptr),
41 maShowPanelFunctor(std::move(aShowPanelFunctor
))
45 FocusManager::~FocusManager()
50 void FocusManager::GrabFocus()
55 void FocusManager::GrabFocusPanel()
60 void FocusManager::Clear()
67 void FocusManager::ClearPanels()
69 SharedPanelContainer aPanels
;
70 aPanels
.swap(maPanels
);
71 for (auto const& panel
: aPanels
)
73 if (panel
->GetTitleBar())
75 UnregisterWindow(panel
->GetTitleBar()->GetToolBox());
76 UnregisterWindow(panel
->GetTitleBar()->GetExpander());
79 weld::Container
* pContents
= panel
->GetContents();
80 UnregisterWindow(*pContents
);
84 void FocusManager::ClearButtons()
86 std::vector
<weld::Widget
*> aButtons
;
87 aButtons
.swap(maButtons
);
88 for (auto const& button
: aButtons
)
90 UnregisterWindow(*button
);
94 void FocusManager::SetDeck(Deck
* pDeck
)
96 DeckTitleBar
* pDeckTitleBar
= pDeck
? pDeck
->GetTitleBar() : nullptr;
97 if (mpDeckTitleBar
!= nullptr)
98 UnregisterWindow(mpDeckTitleBar
->GetToolBox());
100 mpDeckTitleBar
= pDeckTitleBar
;
101 if (mpDeckTitleBar
!= nullptr)
102 RegisterWindow(mpDeckTitleBar
->GetToolBox());
105 void FocusManager::SetPanels (const SharedPanelContainer
& rPanels
)
108 for (auto const& panel
: rPanels
)
110 if (panel
->GetTitleBar())
112 RegisterWindow(panel
->GetTitleBar()->GetToolBox());
113 RegisterWindow(panel
->GetTitleBar()->GetExpander());
116 // Register also as key event listener at the panel.
117 weld::Container
* pContents
= panel
->GetContents();
118 RegisterWindow(*pContents
);
120 maPanels
.emplace_back(panel
);
124 void FocusManager::SetButtons(const std::vector
<weld::Widget
*>& rButtons
)
127 for (auto const& button
: rButtons
)
129 RegisterWindow(*button
);
130 maButtons
.emplace_back(button
);
134 void FocusManager::RegisterWindow(weld::Widget
& rWidget
)
136 UnregisterWindow(rWidget
); // explicitly unset key press handler so we can reconnect without warnings
137 rWidget
.connect_key_press(LINK(this, FocusManager
, KeyInputHdl
));
140 void FocusManager::UnregisterWindow(weld::Widget
& rWidget
)
142 rWidget
.connect_key_press(Link
<const KeyEvent
&, bool>());
145 FocusManager::FocusLocation
FocusManager::GetFocusLocation() const
147 // Check the deck title.
148 if (mpDeckTitleBar
&& mpDeckTitleBar
->GetToolBox().has_focus())
149 return FocusLocation(PC_DeckToolBox
, -1);
151 // Search the panels.
152 for (size_t nIndex
= 0; nIndex
< maPanels
.size(); ++nIndex
)
154 PanelTitleBar
* pTitleBar
= maPanels
[nIndex
]->GetTitleBar();
157 if (pTitleBar
->GetExpander().has_focus())
158 return FocusLocation(PC_PanelTitle
, nIndex
);
159 if (pTitleBar
->GetToolBox().has_focus())
160 return FocusLocation(PC_PanelToolBox
, nIndex
);
161 weld::Container
* pContents
= maPanels
[nIndex
]->GetContents();
162 if (pContents
->has_child_focus())
163 return FocusLocation(PC_PanelContent
, nIndex
);
166 // Search the buttons.
167 for (size_t nIndex
=0; nIndex
< maButtons
.size(); ++nIndex
)
169 if (maButtons
[nIndex
]->has_focus())
170 return FocusLocation(PC_TabBar
, nIndex
);
172 return FocusLocation(PC_None
, -1);
175 void FocusManager::FocusDeckTitle()
177 if (mpDeckTitleBar
!= nullptr)
179 if (mpDeckTitleBar
->GetToolBox().get_n_items() > 0)
181 weld::Toolbar
& rToolBox
= mpDeckTitleBar
->GetToolBox();
182 rToolBox
.grab_focus();
185 FocusPanel(0, false);
188 FocusPanel(0, false);
191 bool FocusManager::IsDeckTitleVisible() const
193 return mpDeckTitleBar
!= nullptr && mpDeckTitleBar
->GetVisible();
196 void FocusManager::FocusPanel (
197 const sal_Int32 nPanelIndex
,
198 const bool bFallbackToDeckTitle
)
200 if (nPanelIndex
<0 || o3tl::make_unsigned(nPanelIndex
)>=maPanels
.size())
202 if (bFallbackToDeckTitle
)
207 Panel
& rPanel (*maPanels
[nPanelIndex
]);
208 PanelTitleBar
* pTitleBar
= rPanel
.GetTitleBar();
209 if (pTitleBar
&& pTitleBar
->GetVisible())
211 rPanel
.SetExpanded(true);
212 pTitleBar
->GetExpander().grab_focus();
214 // Fallback to deck title should only be applicable when there is more than one panel,
215 // or else it will never be possible to enter the panel contents when there's a single panel
216 // without a titlebar and expander
217 else if (bFallbackToDeckTitle
&& maPanels
.size() > 1)
219 // The panel title is not visible, fall back to the deck
221 // Make sure that the desk title is visible here to prevent a
222 // loop when both the title of panel 0 and the deck title are
224 if (IsDeckTitleVisible())
227 FocusPanelContent(nPanelIndex
);
230 FocusPanelContent(nPanelIndex
);
232 if (maShowPanelFunctor
)
233 maShowPanelFunctor(rPanel
);
236 void FocusManager::FocusPanelContent(const sal_Int32 nPanelIndex
)
238 if (!maPanels
[nPanelIndex
]->IsExpanded())
239 maPanels
[nPanelIndex
]->SetExpanded(true);
241 weld::Container
* pContents
= maPanels
[nPanelIndex
]->GetContents();
242 pContents
->child_grab_focus();
245 void FocusManager::FocusButton (const sal_Int32 nButtonIndex
)
247 maButtons
[nButtonIndex
]->grab_focus();
250 void FocusManager::MoveFocusInsidePanel (
251 const FocusLocation
& rFocusLocation
,
252 const sal_Int32 nDirection
)
254 const bool bHasToolBoxItem (
255 maPanels
[rFocusLocation
.mnIndex
]->GetTitleBar()->GetToolBox().get_n_items() > 0);
256 switch (rFocusLocation
.meComponent
)
259 if (nDirection
> 0 && bHasToolBoxItem
)
260 maPanels
[rFocusLocation
.mnIndex
]->GetTitleBar()->GetToolBox().grab_focus();
262 FocusPanelContent(rFocusLocation
.mnIndex
);
265 case PC_PanelToolBox
:
266 if (nDirection
< 0 && bHasToolBoxItem
)
267 maPanels
[rFocusLocation
.mnIndex
]->GetTitleBar()->GetExpander().grab_focus();
269 FocusPanelContent(rFocusLocation
.mnIndex
);
276 bool FocusManager::HandleKeyEvent(
277 const vcl::KeyCode
& rKeyCode
,
278 const FocusLocation
& aLocation
)
280 bool bConsumed
= false;
282 switch (rKeyCode
.GetCode())
285 switch (aLocation
.meComponent
)
290 case PC_PanelToolBox
:
294 mxDeck
->GrabFocusToDocument();
299 case PC_PanelContent
:
300 // Return focus to tab bar sidebar settings button or panel title.
301 if ((!IsDeckTitleVisible() && maPanels
.size() == 1) ||
302 (!maPanels
[aLocation
.mnIndex
]->GetTitleBar()->GetVisible()))
305 FocusPanel(aLocation
.mnIndex
, true);
315 switch (aLocation
.meComponent
)
319 FocusPanelContent(aLocation
.mnIndex
);
330 const sal_Int32
nDirection (
334 switch (aLocation
.meComponent
)
337 case PC_PanelToolBox
:
338 if (rKeyCode
.IsShift())
340 MoveFocusInsidePanel(aLocation
, nDirection
);
346 // Moves to the first deck activation button that is visible and sensitive
348 sal_Int32
nButtons(maButtons
.size());
352 // Finds the next visible button that is sensitive
353 while((!maButtons
[nIndex
]->get_visible() ||
354 !maButtons
[nIndex
]->get_sensitive()) && ++nIndex
< nButtons
);
355 // Wrap to the menu button when going past the last button
356 if (nIndex
>= nButtons
)
365 if (rKeyCode
.IsShift())
367 if (IsDeckTitleVisible())
385 switch (aLocation
.meComponent
)
388 case PC_PanelToolBox
:
389 // Go to previous panel or the deck title.
390 if (aLocation
.mnIndex
> 0)
391 FocusPanel(aLocation
.mnIndex
-1, true);
392 else if (IsDeckTitleVisible())
396 // Set focus to the last visible sensitive button.
397 sal_Int32
nIndex(maButtons
.size()-1);
398 while((!maButtons
[nIndex
]->get_visible() ||
399 !maButtons
[nIndex
]->get_sensitive()) && --nIndex
> 0);
407 if (rKeyCode
.GetCode() == KEY_LEFT
)
411 if (aLocation
.mnIndex
<= 0)
412 nIndex
= maButtons
.size() - 1;
414 nIndex
= aLocation
.mnIndex
- 1;
416 // Finds the previous visible sensitive button
417 while((!maButtons
[nIndex
]->get_visible() ||
418 !maButtons
[nIndex
]->get_sensitive()) && --nIndex
> 0);
431 switch(aLocation
.meComponent
)
434 case PC_PanelToolBox
:
436 if (aLocation
.mnIndex
< static_cast<sal_Int32
>(maPanels
.size())-1)
437 FocusPanel(aLocation
.mnIndex
+1, false);
445 if (rKeyCode
.GetCode() == KEY_RIGHT
)
448 sal_Int32
nButtons(maButtons
.size());
450 sal_Int32 nIndex
= aLocation
.mnIndex
+ 1;
451 if (nIndex
>= nButtons
)
454 // Finds the next visible sensitive button
455 while((!maButtons
[nIndex
]->get_visible() ||
456 !maButtons
[nIndex
]->get_sensitive()) && ++nIndex
< nButtons
);
457 // Wrap to the menu button when going past the last button
458 if (nIndex
>= nButtons
)
473 IMPL_LINK(FocusManager
, KeyInputHdl
, const KeyEvent
&, rKeyEvent
, bool)
475 return HandleKeyEvent(rKeyEvent
.GetKeyCode(), GetFocusLocation());
478 } // end of namespace sfx2::sidebar
480 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */