Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / sidebar / FocusManager.cxx
blob967256a4687539beb13dc3086179779e2c823e62
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 <sfx2/sidebar/FocusManager.hxx>
21 #include <sfx2/sidebar/Panel.hxx>
22 #include <sidebar/DeckTitleBar.hxx>
23 #include <sidebar/PanelTitleBar.hxx>
24 #include <sidebar/TitleBar.hxx>
25 #include <vcl/event.hxx>
26 #include <vcl/svapp.hxx>
27 #include <toolkit/helper/vclunohelper.hxx>
29 namespace sfx2::sidebar {
31 FocusManager::FocusLocation::FocusLocation (const PanelComponent eComponent, const sal_Int32 nIndex)
32 : meComponent(eComponent),
33 mnIndex(nIndex)
37 FocusManager::FocusManager(const std::function<void(const Panel&)>& rShowPanelFunctor)
38 : mpDeckTitleBar(),
39 maPanels(),
40 maButtons(),
41 maShowPanelFunctor(rShowPanelFunctor)
45 FocusManager::~FocusManager()
47 Clear();
50 void FocusManager::GrabFocus()
52 FocusDeckTitle();
55 void FocusManager::GrabFocusPanel()
57 FocusPanel(0, false);
60 void FocusManager::Clear()
62 SetDeckTitle(nullptr);
63 ClearPanels();
64 ClearButtons();
67 void FocusManager::ClearPanels()
69 std::vector<VclPtr<Panel> > 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 panel->RemoveChildEventListener(LINK(this, FocusManager, ChildEventListener));
83 void FocusManager::ClearButtons()
85 std::vector<weld::Widget*> aButtons;
86 aButtons.swap(maButtons);
87 for (auto const& button : aButtons)
89 UnregisterWindow(*button);
93 void FocusManager::SetDeckTitle (DeckTitleBar* pDeckTitleBar)
95 if (mpDeckTitleBar != nullptr)
96 UnregisterWindow(mpDeckTitleBar->GetToolBox());
97 mpDeckTitleBar = pDeckTitleBar;
99 if (mpDeckTitleBar != nullptr)
100 RegisterWindow(mpDeckTitleBar->GetToolBox());
103 void FocusManager::SetPanels (const SharedPanelContainer& rPanels)
105 ClearPanels();
106 for (auto const& panel : rPanels)
108 if (panel->GetTitleBar())
110 RegisterWindow(panel->GetTitleBar()->GetToolBox());
111 RegisterWindow(panel->GetTitleBar()->GetExpander());
114 // Register also as child event listener at the panel.
115 panel->AddChildEventListener(LINK(this, FocusManager, ChildEventListener));
117 maPanels.emplace_back(panel.get());
121 void FocusManager::SetButtons(const std::vector<weld::Widget*>& rButtons)
123 ClearButtons();
124 for (auto const& button : rButtons)
126 RegisterWindow(*button);
127 maButtons.emplace_back(button);
131 void FocusManager::RegisterWindow(weld::Widget& rWidget)
133 UnregisterWindow(rWidget); // explicitly unset key press handler so we can reconnect without warnings
134 rWidget.connect_key_press(LINK(this, FocusManager, KeyInputHdl));
137 void FocusManager::UnregisterWindow(weld::Widget& rWidget)
139 rWidget.connect_key_press(Link<const KeyEvent&, bool>());
142 FocusManager::FocusLocation FocusManager::GetFocusLocation (const vcl::Window& rWindow) const
144 // Search the panels.
145 for (size_t nIndex = 0; nIndex < maPanels.size(); ++nIndex)
147 if (maPanels[nIndex] == &rWindow)
148 return FocusLocation(PC_PanelContent, nIndex);
149 VclPtr<TitleBar> pTitleBar = maPanels[nIndex]->GetTitleBar();
150 if (pTitleBar == &rWindow)
151 return FocusLocation(PC_PanelTitle, nIndex);
154 return FocusLocation(PC_None, -1);
157 FocusManager::FocusLocation FocusManager::GetFocusLocation() const
159 // Check the deck title.
160 if (mpDeckTitleBar && mpDeckTitleBar->GetToolBox().has_focus())
161 return FocusLocation(PC_DeckToolBox, -1);
163 // Search the panels.
164 for (size_t nIndex = 0; nIndex < maPanels.size(); ++nIndex)
166 VclPtr<PanelTitleBar> pTitleBar = maPanels[nIndex]->GetTitleBar();
167 if (!pTitleBar)
168 continue;
169 if (pTitleBar->GetExpander().has_focus())
170 return FocusLocation(PC_PanelTitle, nIndex);
171 if (pTitleBar->GetToolBox().has_focus())
172 return FocusLocation(PC_PanelToolBox, nIndex);
175 // Search the buttons.
176 for (size_t nIndex=0; nIndex < maButtons.size(); ++nIndex)
178 if (maButtons[nIndex]->has_focus())
179 return FocusLocation(PC_TabBar, nIndex);
181 return FocusLocation(PC_None, -1);
184 void FocusManager::FocusDeckTitle()
186 if (mpDeckTitleBar != nullptr)
188 if (mpDeckTitleBar->GetToolBox().get_n_items() > 0)
190 weld::Toolbar& rToolBox = mpDeckTitleBar->GetToolBox();
191 rToolBox.grab_focus();
193 else
194 FocusPanel(0, false);
196 else
197 FocusPanel(0, false);
200 bool FocusManager::IsDeckTitleVisible() const
202 return mpDeckTitleBar != nullptr && mpDeckTitleBar->IsVisible();
205 bool FocusManager::IsPanelTitleVisible (const sal_Int32 nPanelIndex) const
207 if (nPanelIndex<0 || nPanelIndex>=static_cast<sal_Int32>(maPanels.size()))
208 return false;
210 VclPtr<TitleBar> pTitleBar = maPanels[nPanelIndex]->GetTitleBar();
211 if (!pTitleBar)
212 return false;
213 return pTitleBar->IsVisible();
216 void FocusManager::FocusPanel (
217 const sal_Int32 nPanelIndex,
218 const bool bFallbackToDeckTitle)
220 if (nPanelIndex<0 || nPanelIndex>=static_cast<sal_Int32>(maPanels.size()))
222 if (bFallbackToDeckTitle)
223 FocusDeckTitle();
224 return;
227 Panel& rPanel (*maPanels[nPanelIndex]);
228 VclPtr<PanelTitleBar> pTitleBar = rPanel.GetTitleBar();
229 if (pTitleBar && pTitleBar->IsVisible())
231 rPanel.SetExpanded(true);
232 pTitleBar->GetExpander().grab_focus();
234 else if (bFallbackToDeckTitle)
236 // The panel title is not visible, fall back to the deck
237 // title.
238 // Make sure that the desk title is visible here to prevent a
239 // loop when both the title of panel 0 and the deck title are
240 // not present.
241 if (IsDeckTitleVisible())
242 FocusDeckTitle();
243 else
244 FocusPanelContent(nPanelIndex);
246 else
247 FocusPanelContent(nPanelIndex);
249 if (maShowPanelFunctor)
250 maShowPanelFunctor(rPanel);
253 void FocusManager::FocusPanelContent (const sal_Int32 nPanelIndex)
255 if (!maPanels[nPanelIndex]->IsExpanded())
256 maPanels[nPanelIndex]->SetExpanded(true);
258 VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(maPanels[nPanelIndex]->GetElementWindow());
259 if (pWindow)
260 pWindow->GrabFocus();
263 void FocusManager::FocusButton (const sal_Int32 nButtonIndex)
265 maButtons[nButtonIndex]->grab_focus();
268 void FocusManager::RemoveWindow (vcl::Window& rWindow)
270 auto iPanel (::std::find(maPanels.begin(), maPanels.end(), &rWindow));
271 if (iPanel != maPanels.end())
273 if ((*iPanel)->GetTitleBar() != nullptr)
275 UnregisterWindow((*iPanel)->GetTitleBar()->GetToolBox());
276 UnregisterWindow((*iPanel)->GetTitleBar()->GetExpander());
278 maPanels.erase(iPanel);
279 return;
283 void FocusManager::MoveFocusInsidePanel (
284 const FocusLocation& rFocusLocation,
285 const sal_Int32 nDirection)
287 const bool bHasToolBoxItem (
288 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().get_n_items() > 0);
289 switch (rFocusLocation.meComponent)
291 case PC_PanelTitle:
292 if (nDirection > 0 && bHasToolBoxItem)
293 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().grab_focus();
294 else
295 FocusPanelContent(rFocusLocation.mnIndex);
296 break;
298 case PC_PanelToolBox:
299 if (nDirection < 0 && bHasToolBoxItem)
300 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetExpander().grab_focus();
301 else
302 FocusPanelContent(rFocusLocation.mnIndex);
303 break;
305 default: break;
309 void FocusManager::MoveFocusInsideDeckTitle (
310 const FocusLocation& rFocusLocation,
311 const sal_Int32 nDirection)
313 // Note that when the title bar of the first (and only) panel is
314 // not visible then the deck title takes its place and the focus
315 // is moved between a) deck closer and b) content of panel 0.
316 switch (rFocusLocation.meComponent)
318 case PC_DeckToolBox:
319 if (nDirection>0 && ! IsPanelTitleVisible(0))
320 FocusPanelContent(0);
321 break;
323 default: break;
327 bool FocusManager::HandleKeyEvent(
328 const vcl::KeyCode& rKeyCode,
329 const FocusLocation& aLocation)
331 bool bConsumed = false;
333 switch (rKeyCode.GetCode())
335 case KEY_ESCAPE:
336 switch (aLocation.meComponent)
338 case PC_TabBar:
339 case PC_DeckToolBox:
340 case PC_PanelTitle:
341 case PC_PanelToolBox:
343 vcl::Window* pFocusWin = Application::GetFocusWindow();
344 if (pFocusWin)
346 pFocusWin->GrabFocusToDocument();
347 bConsumed = true;
349 break;
352 default:
353 break;
355 return bConsumed;
357 case KEY_RETURN:
358 switch (aLocation.meComponent)
360 case PC_DeckToolBox:
361 FocusButton(0);
362 bConsumed = true;
363 break;
365 case PC_PanelTitle:
366 // Enter the panel.
367 FocusPanelContent(aLocation.mnIndex);
368 bConsumed = true;
369 break;
371 default:
372 break;
374 return bConsumed;
376 case KEY_TAB:
378 const sal_Int32 nDirection (
379 rKeyCode.IsShift()
380 ? -1
381 : +1);
382 switch (aLocation.meComponent)
384 case PC_PanelTitle:
385 case PC_PanelToolBox:
386 case PC_PanelContent:
387 MoveFocusInsidePanel(aLocation, nDirection);
388 bConsumed = true;
389 break;
391 case PC_DeckToolBox:
392 MoveFocusInsideDeckTitle(aLocation, nDirection);
393 bConsumed = true;
394 break;
396 default:
397 break;
399 break;
402 case KEY_LEFT:
403 case KEY_UP:
404 switch (aLocation.meComponent)
406 case PC_PanelTitle:
407 case PC_PanelToolBox:
408 case PC_PanelContent:
409 // Go to previous panel or the deck title.
410 if (aLocation.mnIndex > 0)
411 FocusPanel(aLocation.mnIndex-1, true);
412 else if (IsDeckTitleVisible())
413 FocusDeckTitle();
414 else
416 // Focus the last button.
417 sal_Int32 nIndex(maButtons.size()-1);
418 while(!maButtons[nIndex]->get_visible() && --nIndex > 0);
419 FocusButton(nIndex);
421 bConsumed = true;
422 break;
424 case PC_DeckToolBox:
426 // Focus the last button.
427 sal_Int32 nIndex(maButtons.size()-1);
428 while(!maButtons[nIndex]->get_visible() && --nIndex > 0);
429 FocusButton(nIndex);
430 bConsumed = true;
431 break;
434 case PC_TabBar:
435 // Go to previous tab bar item.
436 if (aLocation.mnIndex == 0)
437 FocusPanel(maPanels.size()-1, true);
438 else
440 sal_Int32 nIndex((aLocation.mnIndex + maButtons.size() - 1) % maButtons.size());
441 while(!maButtons[nIndex]->get_visible() && --nIndex > 0);
442 FocusButton(nIndex);
444 bConsumed = true;
445 break;
447 default:
448 break;
450 break;
452 case KEY_RIGHT:
453 case KEY_DOWN:
454 switch(aLocation.meComponent)
456 case PC_PanelTitle:
457 case PC_PanelToolBox:
458 case PC_PanelContent:
459 // Go to next panel.
460 if (aLocation.mnIndex < static_cast<sal_Int32>(maPanels.size())-1)
461 FocusPanel(aLocation.mnIndex+1, false);
462 else
463 FocusButton(0);
464 bConsumed = true;
465 break;
467 case PC_DeckToolBox:
468 // Focus the first panel.
469 if (IsPanelTitleVisible(0))
470 FocusPanel(0, false);
471 else
472 FocusButton(0);
473 bConsumed = true;
474 break;
476 case PC_TabBar:
477 // Go to next tab bar item.
478 if (aLocation.mnIndex < static_cast<sal_Int32>(maButtons.size())-1)
480 sal_Int32 nIndex(aLocation.mnIndex + 1);
481 while(!maButtons[nIndex]->get_visible() && ++nIndex < static_cast<sal_Int32>(maButtons.size()));
482 if (nIndex < static_cast<sal_Int32>(maButtons.size()))
484 FocusButton(nIndex);
485 bConsumed = true;
486 break;
489 if (IsDeckTitleVisible())
490 FocusDeckTitle();
491 else
492 FocusPanel(0, true);
493 bConsumed = true;
494 break;
496 default:
497 break;
499 break;
501 return bConsumed;
504 IMPL_LINK(FocusManager, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
506 return HandleKeyEvent(rKeyEvent.GetKeyCode(), GetFocusLocation());
509 IMPL_LINK(FocusManager, ChildEventListener, VclWindowEvent&, rEvent, void)
511 vcl::Window* pSource = rEvent.GetWindow();
512 if (pSource == nullptr)
513 return;
515 switch (rEvent.GetId())
517 case VclEventId::WindowKeyInput:
519 KeyEvent* pKeyEvent = static_cast<KeyEvent*>(rEvent.GetData());
521 // Go up the window hierarchy to find out whether the
522 // parent of the event source is known to us.
523 vcl::Window* pWindow = pSource;
524 FocusLocation aLocation (PC_None, -1);
525 while (true)
527 if (pWindow == nullptr)
528 break;
529 aLocation = GetFocusLocation(*pWindow);
530 if (aLocation.meComponent != PC_None)
531 break;
532 pWindow = pWindow->GetParent();
535 if (aLocation.meComponent != PC_None)
537 switch (pKeyEvent->GetKeyCode().GetCode())
539 case KEY_ESCAPE:
540 // Return focus to tab bar sidebar settings button or panel title.
541 if (!IsDeckTitleVisible() && maPanels.size() == 1)
542 FocusButton(0);
543 else
544 FocusPanel(aLocation.mnIndex, true);
545 break;
547 default:
548 break;
551 return;
554 default:
555 break;
559 } // end of namespace sfx2::sidebar
561 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */