LibreOffice Module sfx2 (master) 1
FocusManager.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <o3tl/safeint.hxx>
22#include <sfx2/sidebar/Deck.hxx>
26#include <sidebar/TitleBar.hxx>
27#include <utility>
28#include <vcl/event.hxx>
29#include <vcl/weld.hxx>
30
31namespace sfx2::sidebar {
32
33FocusManager::FocusLocation::FocusLocation (const PanelComponent eComponent, const sal_Int32 nIndex)
34 : meComponent(eComponent),
36{
37}
38
39FocusManager::FocusManager(std::function<void(const Panel&)> aShowPanelFunctor)
40 : mpDeckTitleBar(nullptr),
41 maShowPanelFunctor(std::move(aShowPanelFunctor))
42{
43}
44
46{
47 Clear();
48}
49
51{
53}
54
56{
57 FocusPanel(0, false);
58}
59
61{
62 SetDeck(nullptr);
65}
66
68{
70 aPanels.swap(maPanels);
71 for (auto const& panel : aPanels)
72 {
73 if (panel->GetTitleBar())
74 {
75 UnregisterWindow(panel->GetTitleBar()->GetToolBox());
76 UnregisterWindow(panel->GetTitleBar()->GetExpander());
77 }
78
79 weld::Container* pContents = panel->GetContents();
80 UnregisterWindow(*pContents);
81 }
82}
83
85{
86 std::vector<weld::Widget*> aButtons;
87 aButtons.swap(maButtons);
88 for (auto const& button : aButtons)
89 {
90 UnregisterWindow(*button);
91 }
92}
93
95{
96 DeckTitleBar* pDeckTitleBar = pDeck ? pDeck->GetTitleBar() : nullptr;
97 if (mpDeckTitleBar != nullptr)
99 mxDeck = pDeck;
100 mpDeckTitleBar = pDeckTitleBar;
101 if (mpDeckTitleBar != nullptr)
103}
104
106{
107 ClearPanels();
108 for (auto const& panel : rPanels)
109 {
110 if (panel->GetTitleBar())
111 {
112 RegisterWindow(panel->GetTitleBar()->GetToolBox());
113 RegisterWindow(panel->GetTitleBar()->GetExpander());
114 }
115
116 // Register also as key event listener at the panel.
117 weld::Container* pContents = panel->GetContents();
118 RegisterWindow(*pContents);
119
120 maPanels.emplace_back(panel);
121 }
122}
123
124void FocusManager::SetButtons(const std::vector<weld::Widget*>& rButtons)
125{
126 ClearButtons();
127 for (auto const& button : rButtons)
128 {
129 RegisterWindow(*button);
130 maButtons.emplace_back(button);
131 }
132}
133
135{
136 UnregisterWindow(rWidget); // explicitly unset key press handler so we can reconnect without warnings
137 rWidget.connect_key_press(LINK(this, FocusManager, KeyInputHdl));
138}
139
141{
143}
144
146{
147 // Check the deck title.
149 return FocusLocation(PC_DeckToolBox, -1);
150
151 // Search the panels.
152 for (size_t nIndex = 0; nIndex < maPanels.size(); ++nIndex)
153 {
154 PanelTitleBar* pTitleBar = maPanels[nIndex]->GetTitleBar();
155 if (!pTitleBar)
156 continue;
157 if (pTitleBar->GetExpander().has_focus())
159 if (pTitleBar->GetToolBox().has_focus())
161 weld::Container* pContents = maPanels[nIndex]->GetContents();
162 if (pContents->has_child_focus())
164 }
165
166 // Search the buttons.
167 for (size_t nIndex=0; nIndex < maButtons.size(); ++nIndex)
168 {
169 if (maButtons[nIndex]->has_focus())
171 }
172 return FocusLocation(PC_None, -1);
173}
174
176{
177 if (mpDeckTitleBar != nullptr)
178 {
180 {
182 rToolBox.grab_focus();
183 }
184 else
185 FocusPanel(0, false);
186 }
187 else
188 FocusPanel(0, false);
189}
190
192{
193 return mpDeckTitleBar != nullptr && mpDeckTitleBar->GetVisible();
194}
195
196bool FocusManager::IsPanelTitleVisible (const sal_Int32 nPanelIndex) const
197{
198 if (nPanelIndex<0 || o3tl::make_unsigned(nPanelIndex)>=maPanels.size())
199 return false;
200
201 TitleBar* pTitleBar = maPanels[nPanelIndex]->GetTitleBar();
202 if (!pTitleBar)
203 return false;
204 return pTitleBar->GetVisible();
205}
206
208 const sal_Int32 nPanelIndex,
209 const bool bFallbackToDeckTitle)
210{
211 if (nPanelIndex<0 || o3tl::make_unsigned(nPanelIndex)>=maPanels.size())
212 {
213 if (bFallbackToDeckTitle)
215 return;
216 }
217
218 Panel& rPanel (*maPanels[nPanelIndex]);
219 PanelTitleBar* pTitleBar = rPanel.GetTitleBar();
220 if (pTitleBar && pTitleBar->GetVisible())
221 {
222 rPanel.SetExpanded(true);
223 pTitleBar->GetExpander().grab_focus();
224 }
225 else if (bFallbackToDeckTitle)
226 {
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())
234 else
235 FocusPanelContent(nPanelIndex);
236 }
237 else
238 FocusPanelContent(nPanelIndex);
239
241 maShowPanelFunctor(rPanel);
242}
243
244void FocusManager::FocusPanelContent(const sal_Int32 nPanelIndex)
245{
246 if (!maPanels[nPanelIndex]->IsExpanded())
247 maPanels[nPanelIndex]->SetExpanded(true);
248
249 weld::Container* pContents = maPanels[nPanelIndex]->GetContents();
250 pContents->child_grab_focus();
251}
252
253void FocusManager::FocusButton (const sal_Int32 nButtonIndex)
254{
255 maButtons[nButtonIndex]->grab_focus();
256}
257
259 const FocusLocation& rFocusLocation,
260 const sal_Int32 nDirection)
261{
262 const bool bHasToolBoxItem (
263 maPanels[rFocusLocation.mnIndex]->GetTitleBar()->GetToolBox().get_n_items() > 0);
264 switch (rFocusLocation.meComponent)
265 {
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;
272
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;
279
280 default: break;
281 }
282}
283
285 const FocusLocation& rFocusLocation,
286 const sal_Int32 nDirection)
287{
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)
293 {
294 case PC_DeckToolBox:
295 if (nDirection>0 && ! IsPanelTitleVisible(0))
296 {
298 bConsumed = true;
299 }
300 break;
301
302 default: break;
303 }
304 return bConsumed;
305}
306
308 const vcl::KeyCode& rKeyCode,
309 const FocusLocation& aLocation)
310{
311 bool bConsumed = false;
312
313 switch (rKeyCode.GetCode())
314 {
315 case KEY_ESCAPE:
316 switch (aLocation.meComponent)
317 {
318 case PC_TabBar:
319 case PC_DeckToolBox:
320 case PC_PanelTitle:
321 case PC_PanelToolBox:
322 {
323 if (mxDeck)
324 {
325 mxDeck->GrabFocusToDocument();
326 bConsumed = true;
327 }
328 break;
329 }
330 case PC_PanelContent:
331 // Return focus to tab bar sidebar settings button or panel title.
332 if (!IsDeckTitleVisible() && maPanels.size() == 1)
333 FocusButton(0);
334 else
335 FocusPanel(aLocation.mnIndex, true);
336 bConsumed = true;
337 break;
338 default:
339 break;
340 }
341 return bConsumed;
342
343 case KEY_RETURN:
344 switch (aLocation.meComponent)
345 {
346 case PC_DeckToolBox:
347 FocusButton(0);
348 bConsumed = true;
349 break;
350
351 case PC_PanelTitle:
352 // Enter the panel.
353 FocusPanelContent(aLocation.mnIndex);
354 bConsumed = true;
355 break;
356
357 default:
358 break;
359 }
360 return bConsumed;
361
362 case KEY_TAB:
363 {
364 const sal_Int32 nDirection (
365 rKeyCode.IsShift()
366 ? -1
367 : +1);
368 switch (aLocation.meComponent)
369 {
370 case PC_PanelTitle:
371 case PC_PanelToolBox:
372 MoveFocusInsidePanel(aLocation, nDirection);
373 bConsumed = true;
374 break;
375
376 case PC_DeckToolBox:
377 bConsumed = MoveFocusInsideDeckTitle(aLocation, nDirection);
378 break;
379
380 case PC_TabBar:
381 if (rKeyCode.IsShift())
382 FocusPanel(maPanels.size()-1, true);
383 else
384 {
385 if (IsDeckTitleVisible())
387 else
388 FocusPanel(0, true);
389 }
390 bConsumed = true;
391 break;
392
393 default:
394 break;
395 }
396 break;
397 }
398
399 case KEY_LEFT:
400 case KEY_UP:
401 switch (aLocation.meComponent)
402 {
403 case PC_PanelTitle:
404 case PC_PanelToolBox:
405 // Go to previous panel or the deck title.
406 if (aLocation.mnIndex > 0)
407 FocusPanel(aLocation.mnIndex-1, true);
408 else if (IsDeckTitleVisible())
410 else
411 {
412 // Focus the last button.
413 sal_Int32 nIndex(maButtons.size()-1);
414 while(!maButtons[nIndex]->get_visible() && --nIndex > 0);
416 }
417 bConsumed = true;
418 break;
419
420 case PC_DeckToolBox:
421 {
422 // Focus the last button.
423 sal_Int32 nIndex(maButtons.size()-1);
424 while(!maButtons[nIndex]->get_visible() && --nIndex > 0);
426 bConsumed = true;
427 break;
428 }
429
430 case PC_TabBar:
431 // Go to previous tab bar item.
432 if (aLocation.mnIndex == 0)
433 FocusPanel(maPanels.size()-1, true);
434 else
435 {
436 sal_Int32 nIndex((aLocation.mnIndex + maButtons.size() - 1) % maButtons.size());
437 while(!maButtons[nIndex]->get_visible() && --nIndex > 0);
439 }
440 bConsumed = true;
441 break;
442
443 default:
444 break;
445 }
446 break;
447
448 case KEY_RIGHT:
449 case KEY_DOWN:
450 switch(aLocation.meComponent)
451 {
452 case PC_PanelTitle:
453 case PC_PanelToolBox:
454 // Go to next panel.
455 if (aLocation.mnIndex < static_cast<sal_Int32>(maPanels.size())-1)
456 FocusPanel(aLocation.mnIndex+1, false);
457 else
458 FocusButton(0);
459 bConsumed = true;
460 break;
461
462 case PC_DeckToolBox:
463 // Focus the first panel.
464 if (IsPanelTitleVisible(0))
465 FocusPanel(0, false);
466 else
467 FocusButton(0);
468 bConsumed = true;
469 break;
470
471 case PC_TabBar:
472 // Go to next tab bar item.
473 if (aLocation.mnIndex < static_cast<sal_Int32>(maButtons.size())-1)
474 {
475 sal_Int32 nIndex(aLocation.mnIndex + 1);
476 while(!maButtons[nIndex]->get_visible() && ++nIndex < static_cast<sal_Int32>(maButtons.size()));
477 if (nIndex < static_cast<sal_Int32>(maButtons.size()))
478 {
480 bConsumed = true;
481 break;
482 }
483 }
484 if (IsDeckTitleVisible())
486 else
487 FocusPanel(0, true);
488 bConsumed = true;
489 break;
490
491 default:
492 break;
493 }
494 break;
495 }
496 return bConsumed;
497}
498
499IMPL_LINK(FocusManager, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
500{
501 return HandleKeyEvent(rKeyEvent.GetKeyCode(), GetFocusLocation());
502}
503
504} // end of namespace sfx2::sidebar
505
506/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
This is the parent window of the panels.
Definition: Deck.hxx:38
DeckTitleBar * GetTitleBar() const
Definition: Deck.cxx:105
FocusLocation(const PanelComponent eComponent, const sal_Int32 nIndex)
Concentrate all focus handling in this class.
bool IsPanelTitleVisible(const sal_Int32 nPanelIndex) const
void SetButtons(const std::vector< weld::Widget * > &rButtons)
const std::function< void(const Panel &)> maShowPanelFunctor
void GrabFocus()
Transfer the focus into the sidebar tree of windows.
bool HandleKeyEvent(const vcl::KeyCode &rKeyCode, const FocusLocation &rLocation)
void FocusButton(const sal_Int32 nButtonIndex)
bool MoveFocusInsideDeckTitle(const FocusLocation &rLocation, const sal_Int32 nDirection)
FocusLocation GetFocusLocation() const
void FocusPanel(const sal_Int32 nPanelIndex, const bool bFallbackToDeckTitle)
Set the focus to the title bar of the panel or, if the title bar is not visible, directly to the pane...
void SetPanels(const SharedPanelContainer &rPanels)
static void UnregisterWindow(weld::Widget &rWidget)
SharedPanelContainer maPanels
void Clear()
Forget all panels and buttons.
FocusManager(std::function< void(const Panel &)> aShowPanelFunctor)
void FocusPanelContent(const sal_Int32 nPanelIndex)
std::vector< weld::Widget * > maButtons
void MoveFocusInsidePanel(const FocusLocation &rLocation, const sal_Int32 nDirection)
void SetDeck(Deck *pDeck)
void RegisterWindow(weld::Widget &rWidget)
Let the focus manager listen for window events for the given window.
weld::Expander & GetExpander()
Multiple panels form a single deck.
Definition: Panel.hxx:54
void SetExpanded(const bool bIsExpanded)
Definition: Panel.cxx:183
PanelTitleBar * GetTitleBar() const
Definition: Panel.cxx:145
bool GetVisible() const
Definition: TitleBar.cxx:62
weld::Toolbar & GetToolBox()
Definition: TitleBar.hxx:44
sal_uInt16 GetCode() const
bool IsShift() const
virtual void child_grab_focus()=0
virtual int get_n_items() const=0
virtual void grab_focus()=0
virtual bool has_focus() const=0
virtual void connect_key_press(const Link< const KeyEvent &, bool > &rLink)
virtual bool has_child_focus() const=0
sal_Int32 nIndex
constexpr sal_uInt16 KEY_RETURN
constexpr sal_uInt16 KEY_ESCAPE
constexpr sal_uInt16 KEY_LEFT
constexpr sal_uInt16 KEY_TAB
constexpr sal_uInt16 KEY_UP
constexpr sal_uInt16 KEY_RIGHT
constexpr sal_uInt16 KEY_DOWN
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
std::vector< std::shared_ptr< Panel > > SharedPanelContainer
Definition: Panel.hxx:118
IMPL_LINK(FocusManager, KeyInputHdl, const KeyEvent &, rKeyEvent, bool)
sal_uInt32 mnIndex