Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sfx2 / source / sidebar / FocusManager.cxx
blobb77d30a75ba3fba259cd055e258b4aa992b7aa9a
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 bool FocusManager::IsPanelTitleVisible (const sal_Int32 nPanelIndex) const
198 if (nPanelIndex<0 || o3tl::make_unsigned(nPanelIndex)>=maPanels.size())
199 return false;
201 TitleBar* pTitleBar = maPanels[nPanelIndex]->GetTitleBar();
202 if (!pTitleBar)
203 return false;
204 return pTitleBar->GetVisible();
207 void FocusManager::FocusPanel (
208 const sal_Int32 nPanelIndex,
209 const bool bFallbackToDeckTitle)
211 if (nPanelIndex<0 || o3tl::make_unsigned(nPanelIndex)>=maPanels.size())
213 if (bFallbackToDeckTitle)
214 FocusDeckTitle();
215 return;
218 Panel& rPanel (*maPanels[nPanelIndex]);
219 PanelTitleBar* pTitleBar = rPanel.GetTitleBar();
220 if (pTitleBar && pTitleBar->GetVisible())
222 rPanel.SetExpanded(true);
223 pTitleBar->GetExpander().grab_focus();
225 else if (bFallbackToDeckTitle)
227 // The panel title is not visible, fall back to the deck
228 // title.
229 // Make sure that the desk title is visible here to prevent a
230 // loop when both the title of panel 0 and the deck title are
231 // not present.
232 if (IsDeckTitleVisible())
233 FocusDeckTitle();
234 else
235 FocusPanelContent(nPanelIndex);
237 else
238 FocusPanelContent(nPanelIndex);
240 if (maShowPanelFunctor)
241 maShowPanelFunctor(rPanel);
244 void FocusManager::FocusPanelContent(const sal_Int32 nPanelIndex)
246 if (!maPanels[nPanelIndex]->IsExpanded())
247 maPanels[nPanelIndex]->SetExpanded(true);
249 weld::Container* pContents = maPanels[nPanelIndex]->GetContents();
250 pContents->child_grab_focus();
253 void FocusManager::FocusButton (const sal_Int32 nButtonIndex)
255 maButtons[nButtonIndex]->grab_focus();
258 void FocusManager::MoveFocusInsidePanel (
259 const FocusLocation& rFocusLocation,
260 const sal_Int32 nDirection)
262 const bool bHasToolBoxItem (
263 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().get_n_items() > 0);
264 switch (rFocusLocation.meComponent)
266 case PC_PanelTitle:
267 if (nDirection > 0 && bHasToolBoxItem)
268 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().grab_focus();
269 else
270 FocusPanelContent(rFocusLocation.mnIndex);
271 break;
273 case PC_PanelToolBox:
274 if (nDirection < 0 && bHasToolBoxItem)
275 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetExpander().grab_focus();
276 else
277 FocusPanelContent(rFocusLocation.mnIndex);
278 break;
280 default: break;
284 bool FocusManager::MoveFocusInsideDeckTitle (
285 const FocusLocation& rFocusLocation,
286 const sal_Int32 nDirection)
288 bool bConsumed = false;
289 // Note that when the title bar of the first (and only) panel is
290 // not visible then the deck title takes its place and the focus
291 // is moved between a) deck closer and b) content of panel 0.
292 switch (rFocusLocation.meComponent)
294 case PC_DeckToolBox:
295 if (nDirection>0 && ! IsPanelTitleVisible(0))
297 FocusPanelContent(0);
298 bConsumed = true;
300 else if (nDirection < 0)
302 FocusButton(0);
303 bConsumed = true;
305 break;
307 default: break;
309 return bConsumed;
312 bool FocusManager::HandleKeyEvent(
313 const vcl::KeyCode& rKeyCode,
314 const FocusLocation& aLocation)
316 bool bConsumed = false;
318 switch (rKeyCode.GetCode())
320 case KEY_ESCAPE:
321 switch (aLocation.meComponent)
323 case PC_TabBar:
324 case PC_DeckToolBox:
325 case PC_PanelTitle:
326 case PC_PanelToolBox:
328 if (mxDeck)
330 mxDeck->GrabFocusToDocument();
331 bConsumed = true;
333 break;
335 case PC_PanelContent:
336 // Return focus to tab bar sidebar settings button or panel title.
337 if (!IsDeckTitleVisible() && maPanels.size() == 1)
338 FocusButton(0);
339 else
340 FocusPanel(aLocation.mnIndex, true);
341 bConsumed = true;
342 break;
343 default:
344 break;
346 return bConsumed;
348 case KEY_RETURN:
349 switch (aLocation.meComponent)
351 case PC_DeckToolBox:
352 FocusButton(0);
353 bConsumed = true;
354 break;
356 case PC_PanelTitle:
357 // Enter the panel.
358 FocusPanelContent(aLocation.mnIndex);
359 bConsumed = true;
360 break;
362 default:
363 break;
365 return bConsumed;
367 case KEY_TAB:
369 const sal_Int32 nDirection (
370 rKeyCode.IsShift()
371 ? -1
372 : +1);
373 switch (aLocation.meComponent)
375 case PC_PanelTitle:
376 case PC_PanelToolBox:
377 if (rKeyCode.IsShift())
378 break;
379 MoveFocusInsidePanel(aLocation, nDirection);
380 bConsumed = true;
381 break;
383 case PC_DeckToolBox:
384 bConsumed = MoveFocusInsideDeckTitle(aLocation, nDirection);
385 break;
387 case PC_TabBar:
388 if (rKeyCode.IsShift())
389 FocusPanel(maPanels.size()-1, true);
390 else
392 if (IsDeckTitleVisible())
393 FocusDeckTitle();
394 else
395 FocusPanel(0, true);
397 bConsumed = true;
398 break;
400 default:
401 break;
403 break;
406 case KEY_LEFT:
407 case KEY_UP:
408 switch (aLocation.meComponent)
410 case PC_PanelTitle:
411 case PC_PanelToolBox:
412 // Go to previous panel or the deck title.
413 if (aLocation.mnIndex > 0)
414 FocusPanel(aLocation.mnIndex-1, true);
415 else if (IsDeckTitleVisible())
416 FocusDeckTitle();
417 else
419 // Focus the last button.
420 sal_Int32 nIndex(maButtons.size()-1);
421 while(!maButtons[nIndex]->get_visible() && --nIndex > 0);
422 FocusButton(nIndex);
424 bConsumed = true;
425 break;
427 case PC_DeckToolBox:
429 // Focus the last button.
430 sal_Int32 nIndex(maButtons.size()-1);
431 while(!maButtons[nIndex]->get_visible() && --nIndex > 0);
432 FocusButton(nIndex);
433 bConsumed = true;
434 break;
437 case PC_TabBar:
438 // Go to previous tab bar item.
439 if (aLocation.mnIndex == 0)
440 FocusPanel(maPanels.size()-1, true);
441 else
443 sal_Int32 nIndex((aLocation.mnIndex + maButtons.size() - 1) % maButtons.size());
444 while(!maButtons[nIndex]->get_visible() && --nIndex > 0);
445 FocusButton(nIndex);
447 bConsumed = true;
448 break;
450 default:
451 break;
453 break;
455 case KEY_RIGHT:
456 case KEY_DOWN:
457 switch(aLocation.meComponent)
459 case PC_PanelTitle:
460 case PC_PanelToolBox:
461 // Go to next panel.
462 if (aLocation.mnIndex < static_cast<sal_Int32>(maPanels.size())-1)
463 FocusPanel(aLocation.mnIndex+1, false);
464 else
465 FocusButton(0);
466 bConsumed = true;
467 break;
469 case PC_DeckToolBox:
470 // Focus the first panel.
471 if (IsPanelTitleVisible(0))
472 FocusPanel(0, false);
473 else
474 FocusButton(0);
475 bConsumed = true;
476 break;
478 case PC_TabBar:
479 // Go to next tab bar item.
480 if (aLocation.mnIndex < static_cast<sal_Int32>(maButtons.size())-1)
482 sal_Int32 nIndex(aLocation.mnIndex + 1);
483 while(!maButtons[nIndex]->get_visible() && ++nIndex < static_cast<sal_Int32>(maButtons.size()));
484 if (nIndex < static_cast<sal_Int32>(maButtons.size()))
486 FocusButton(nIndex);
487 bConsumed = true;
488 break;
491 if (IsDeckTitleVisible())
492 FocusDeckTitle();
493 else
494 FocusPanel(0, true);
495 bConsumed = true;
496 break;
498 default:
499 break;
501 break;
503 return bConsumed;
506 IMPL_LINK(FocusManager, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
508 return HandleKeyEvent(rKeyEvent.GetKeyCode(), GetFocusLocation());
511 } // end of namespace sfx2::sidebar
513 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */