use insert function instead of for loop
[LibreOffice.git] / sfx2 / source / sidebar / FocusManager.cxx
blob806ac3d3a82321d093030d0900276b64b181e743
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
27 #include <utility>
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),
35 mnIndex(nIndex)
39 FocusManager::FocusManager(std::function<void(const Panel&)> aShowPanelFunctor)
40 : mpDeckTitleBar(nullptr),
41 maShowPanelFunctor(std::move(aShowPanelFunctor))
45 FocusManager::~FocusManager()
47 Clear();
50 void FocusManager::GrabFocus()
52 FocusDeckTitle();
55 void FocusManager::GrabFocusPanel()
57 FocusPanel(0, false);
60 void FocusManager::Clear()
62 SetDeck(nullptr);
63 ClearPanels();
64 ClearButtons();
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());
99 mxDeck = pDeck;
100 mpDeckTitleBar = pDeckTitleBar;
101 if (mpDeckTitleBar != nullptr)
102 RegisterWindow(mpDeckTitleBar->GetToolBox());
105 void FocusManager::SetPanels (const SharedPanelContainer& rPanels)
107 ClearPanels();
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)
126 ClearButtons();
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();
155 if (!pTitleBar)
156 continue;
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();
184 else
185 FocusPanel(0, false);
187 else
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)
203 FocusDeckTitle();
204 return;
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
220 // title.
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
223 // not present.
224 if (IsDeckTitleVisible())
225 FocusDeckTitle();
226 else
227 FocusPanelContent(nPanelIndex);
229 else
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)
258 case PC_PanelTitle:
259 if (nDirection > 0 && bHasToolBoxItem)
260 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().grab_focus();
261 else
262 FocusPanelContent(rFocusLocation.mnIndex);
263 break;
265 case PC_PanelToolBox:
266 if (nDirection < 0 && bHasToolBoxItem)
267 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetExpander().grab_focus();
268 else
269 FocusPanelContent(rFocusLocation.mnIndex);
270 break;
272 default: break;
276 bool FocusManager::HandleKeyEvent(
277 const vcl::KeyCode& rKeyCode,
278 const FocusLocation& aLocation)
280 bool bConsumed = false;
282 switch (rKeyCode.GetCode())
284 case KEY_ESCAPE:
285 switch (aLocation.meComponent)
287 case PC_TabBar:
288 case PC_DeckToolBox:
289 case PC_PanelTitle:
290 case PC_PanelToolBox:
292 if (mxDeck)
294 mxDeck->GrabFocusToDocument();
295 bConsumed = true;
297 break;
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()))
303 FocusButton(0);
304 else
305 FocusPanel(aLocation.mnIndex, true);
307 bConsumed = true;
308 break;
309 default:
310 break;
312 return bConsumed;
314 case KEY_RETURN:
315 switch (aLocation.meComponent)
317 case PC_PanelTitle:
318 // Enter the panel.
319 FocusPanelContent(aLocation.mnIndex);
320 bConsumed = true;
321 break;
323 default:
324 break;
326 return bConsumed;
328 case KEY_TAB:
330 const sal_Int32 nDirection (
331 rKeyCode.IsShift()
332 ? -1
333 : +1);
334 switch (aLocation.meComponent)
336 case PC_PanelTitle:
337 case PC_PanelToolBox:
338 if (rKeyCode.IsShift())
339 break;
340 MoveFocusInsidePanel(aLocation, nDirection);
341 bConsumed = true;
342 break;
344 case PC_DeckToolBox:
346 // Moves to the first deck activation button that is visible and sensitive
347 sal_Int32 nIndex(0);
348 sal_Int32 nButtons(maButtons.size());
349 if (nButtons > 1)
351 nIndex = 1;
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)
357 nIndex = 0;
359 FocusButton(nIndex);
360 bConsumed = true;
362 break;
364 case PC_TabBar:
365 if (rKeyCode.IsShift())
367 if (IsDeckTitleVisible())
368 FocusDeckTitle();
369 else
370 FocusPanel(0, true);
372 else
373 FocusPanel(0, true);
374 bConsumed = true;
375 break;
377 default:
378 break;
380 break;
383 case KEY_LEFT:
384 case KEY_UP:
385 switch (aLocation.meComponent)
387 case PC_PanelTitle:
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())
393 FocusDeckTitle();
394 else
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);
400 FocusButton(nIndex);
402 bConsumed = true;
403 break;
405 case PC_TabBar:
407 if (rKeyCode.GetCode() == KEY_LEFT)
408 break;
410 sal_Int32 nIndex;
411 if (aLocation.mnIndex <= 0)
412 nIndex = maButtons.size() - 1;
413 else
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);
419 FocusButton(nIndex);
420 bConsumed = true;
422 break;
424 default:
425 break;
427 break;
429 case KEY_RIGHT:
430 case KEY_DOWN:
431 switch(aLocation.meComponent)
433 case PC_PanelTitle:
434 case PC_PanelToolBox:
435 // Go to next panel.
436 if (aLocation.mnIndex < static_cast<sal_Int32>(maPanels.size())-1)
437 FocusPanel(aLocation.mnIndex+1, false);
438 else
439 FocusButton(0);
440 bConsumed = true;
441 break;
443 case PC_TabBar:
445 if (rKeyCode.GetCode() == KEY_RIGHT)
446 break;
448 sal_Int32 nButtons(maButtons.size());
450 sal_Int32 nIndex = aLocation.mnIndex + 1;
451 if (nIndex >= nButtons)
452 nIndex = 0;
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)
459 nIndex = 0;
460 FocusButton(nIndex);
461 bConsumed = true;
463 break;
465 default:
466 break;
468 break;
470 return bConsumed;
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: */