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 bool FocusManager::IsPanelTitleVisible (const sal_Int32 nPanelIndex
) const
198 if (nPanelIndex
<0 || o3tl::make_unsigned(nPanelIndex
)>=maPanels
.size())
201 TitleBar
* pTitleBar
= maPanels
[nPanelIndex
]->GetTitleBar();
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
)
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
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
232 if (IsDeckTitleVisible())
235 FocusPanelContent(nPanelIndex
);
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
)
267 if (nDirection
> 0 && bHasToolBoxItem
)
268 maPanels
[rFocusLocation
.mnIndex
]->GetTitleBar()->GetToolBox().grab_focus();
270 FocusPanelContent(rFocusLocation
.mnIndex
);
273 case PC_PanelToolBox
:
274 if (nDirection
< 0 && bHasToolBoxItem
)
275 maPanels
[rFocusLocation
.mnIndex
]->GetTitleBar()->GetExpander().grab_focus();
277 FocusPanelContent(rFocusLocation
.mnIndex
);
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
)
295 if (nDirection
>0 && ! IsPanelTitleVisible(0))
297 FocusPanelContent(0);
300 else if (nDirection
< 0)
312 bool FocusManager::HandleKeyEvent(
313 const vcl::KeyCode
& rKeyCode
,
314 const FocusLocation
& aLocation
)
316 bool bConsumed
= false;
318 switch (rKeyCode
.GetCode())
321 switch (aLocation
.meComponent
)
326 case PC_PanelToolBox
:
330 mxDeck
->GrabFocusToDocument();
335 case PC_PanelContent
:
336 // Return focus to tab bar sidebar settings button or panel title.
337 if (!IsDeckTitleVisible() && maPanels
.size() == 1)
340 FocusPanel(aLocation
.mnIndex
, true);
349 switch (aLocation
.meComponent
)
358 FocusPanelContent(aLocation
.mnIndex
);
369 const sal_Int32
nDirection (
373 switch (aLocation
.meComponent
)
376 case PC_PanelToolBox
:
377 if (rKeyCode
.IsShift())
379 MoveFocusInsidePanel(aLocation
, nDirection
);
384 bConsumed
= MoveFocusInsideDeckTitle(aLocation
, nDirection
);
388 if (rKeyCode
.IsShift())
389 FocusPanel(maPanels
.size()-1, true);
392 if (IsDeckTitleVisible())
408 switch (aLocation
.meComponent
)
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())
419 // Focus the last button.
420 sal_Int32
nIndex(maButtons
.size()-1);
421 while(!maButtons
[nIndex
]->get_visible() && --nIndex
> 0);
429 // Focus the last button.
430 sal_Int32
nIndex(maButtons
.size()-1);
431 while(!maButtons
[nIndex
]->get_visible() && --nIndex
> 0);
438 // Go to previous tab bar item.
439 if (aLocation
.mnIndex
== 0)
440 FocusPanel(maPanels
.size()-1, true);
443 sal_Int32
nIndex((aLocation
.mnIndex
+ maButtons
.size() - 1) % maButtons
.size());
444 while(!maButtons
[nIndex
]->get_visible() && --nIndex
> 0);
457 switch(aLocation
.meComponent
)
460 case PC_PanelToolBox
:
462 if (aLocation
.mnIndex
< static_cast<sal_Int32
>(maPanels
.size())-1)
463 FocusPanel(aLocation
.mnIndex
+1, false);
470 // Focus the first panel.
471 if (IsPanelTitleVisible(0))
472 FocusPanel(0, false);
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()))
491 if (IsDeckTitleVisible())
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: */