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 <sfx2/sidebar/FocusManager.hxx>
21 #include <sfx2/sidebar/Deck.hxx>
22 #include <sfx2/sidebar/Panel.hxx>
23 #include <sidebar/DeckTitleBar.hxx>
24 #include <sidebar/PanelTitleBar.hxx>
25 #include <sidebar/TitleBar.hxx>
26 #include <vcl/event.hxx>
27 #include <vcl/weld.hxx>
29 namespace sfx2::sidebar
{
31 FocusManager::FocusLocation::FocusLocation (const PanelComponent eComponent
, const sal_Int32 nIndex
)
32 : meComponent(eComponent
),
37 FocusManager::FocusManager(const std::function
<void(const Panel
&)>& rShowPanelFunctor
)
38 : mpDeckTitleBar(nullptr),
39 maShowPanelFunctor(rShowPanelFunctor
)
43 FocusManager::~FocusManager()
48 void FocusManager::GrabFocus()
53 void FocusManager::GrabFocusPanel()
58 void FocusManager::Clear()
65 void FocusManager::ClearPanels()
67 SharedPanelContainer aPanels
;
68 aPanels
.swap(maPanels
);
69 for (auto const& panel
: aPanels
)
71 if (panel
->GetTitleBar())
73 UnregisterWindow(panel
->GetTitleBar()->GetToolBox());
74 UnregisterWindow(panel
->GetTitleBar()->GetExpander());
77 weld::Container
* pContents
= panel
->GetContents();
78 UnregisterWindow(*pContents
);
82 void FocusManager::ClearButtons()
84 std::vector
<weld::Widget
*> aButtons
;
85 aButtons
.swap(maButtons
);
86 for (auto const& button
: aButtons
)
88 UnregisterWindow(*button
);
92 void FocusManager::SetDeck(Deck
* pDeck
)
94 DeckTitleBar
* pDeckTitleBar
= pDeck
? pDeck
->GetTitleBar() : nullptr;
95 if (mpDeckTitleBar
!= nullptr)
96 UnregisterWindow(mpDeckTitleBar
->GetToolBox());
98 mpDeckTitleBar
= pDeckTitleBar
;
99 if (mpDeckTitleBar
!= nullptr)
100 RegisterWindow(mpDeckTitleBar
->GetToolBox());
103 void FocusManager::SetPanels (const SharedPanelContainer
& rPanels
)
106 for (auto const& panel
: rPanels
)
108 if (panel
->GetTitleBar())
110 RegisterWindow(panel
->GetTitleBar()->GetToolBox());
111 RegisterWindow(panel
->GetTitleBar()->GetExpander());
114 // Register also as key event listener at the panel.
115 weld::Container
* pContents
= panel
->GetContents();
116 RegisterWindow(*pContents
);
118 maPanels
.emplace_back(panel
);
122 void FocusManager::SetButtons(const std::vector
<weld::Widget
*>& rButtons
)
125 for (auto const& button
: rButtons
)
127 RegisterWindow(*button
);
128 maButtons
.emplace_back(button
);
132 void FocusManager::RegisterWindow(weld::Widget
& rWidget
)
134 UnregisterWindow(rWidget
); // explicitly unset key press handler so we can reconnect without warnings
135 rWidget
.connect_key_press(LINK(this, FocusManager
, KeyInputHdl
));
138 void FocusManager::UnregisterWindow(weld::Widget
& rWidget
)
140 rWidget
.connect_key_press(Link
<const KeyEvent
&, bool>());
143 FocusManager::FocusLocation
FocusManager::GetFocusLocation() const
145 // Check the deck title.
146 if (mpDeckTitleBar
&& mpDeckTitleBar
->GetToolBox().has_focus())
147 return FocusLocation(PC_DeckToolBox
, -1);
149 // Search the panels.
150 for (size_t nIndex
= 0; nIndex
< maPanels
.size(); ++nIndex
)
152 PanelTitleBar
* pTitleBar
= maPanels
[nIndex
]->GetTitleBar();
155 if (pTitleBar
->GetExpander().has_focus())
156 return FocusLocation(PC_PanelTitle
, nIndex
);
157 if (pTitleBar
->GetToolBox().has_focus())
158 return FocusLocation(PC_PanelToolBox
, nIndex
);
159 weld::Container
* pContents
= maPanels
[nIndex
]->GetContents();
160 if (pContents
->has_child_focus())
161 return FocusLocation(PC_PanelContent
, nIndex
);
164 // Search the buttons.
165 for (size_t nIndex
=0; nIndex
< maButtons
.size(); ++nIndex
)
167 if (maButtons
[nIndex
]->has_focus())
168 return FocusLocation(PC_TabBar
, nIndex
);
170 return FocusLocation(PC_None
, -1);
173 void FocusManager::FocusDeckTitle()
175 if (mpDeckTitleBar
!= nullptr)
177 if (mpDeckTitleBar
->GetToolBox().get_n_items() > 0)
179 weld::Toolbar
& rToolBox
= mpDeckTitleBar
->GetToolBox();
180 rToolBox
.grab_focus();
183 FocusPanel(0, false);
186 FocusPanel(0, false);
189 bool FocusManager::IsDeckTitleVisible() const
191 return mpDeckTitleBar
!= nullptr && mpDeckTitleBar
->GetVisible();
194 bool FocusManager::IsPanelTitleVisible (const sal_Int32 nPanelIndex
) const
196 if (nPanelIndex
<0 || nPanelIndex
>=static_cast<sal_Int32
>(maPanels
.size()))
199 TitleBar
* pTitleBar
= maPanels
[nPanelIndex
]->GetTitleBar();
202 return pTitleBar
->GetVisible();
205 void FocusManager::FocusPanel (
206 const sal_Int32 nPanelIndex
,
207 const bool bFallbackToDeckTitle
)
209 if (nPanelIndex
<0 || nPanelIndex
>=static_cast<sal_Int32
>(maPanels
.size()))
211 if (bFallbackToDeckTitle
)
216 Panel
& rPanel (*maPanels
[nPanelIndex
]);
217 PanelTitleBar
* pTitleBar
= rPanel
.GetTitleBar();
218 if (pTitleBar
&& pTitleBar
->GetVisible())
220 rPanel
.SetExpanded(true);
221 pTitleBar
->GetExpander().grab_focus();
223 else if (bFallbackToDeckTitle
)
225 // The panel title is not visible, fall back to the deck
227 // Make sure that the desk title is visible here to prevent a
228 // loop when both the title of panel 0 and the deck title are
230 if (IsDeckTitleVisible())
233 FocusPanelContent(nPanelIndex
);
236 FocusPanelContent(nPanelIndex
);
238 if (maShowPanelFunctor
)
239 maShowPanelFunctor(rPanel
);
242 void FocusManager::FocusPanelContent(const sal_Int32 nPanelIndex
)
244 if (!maPanels
[nPanelIndex
]->IsExpanded())
245 maPanels
[nPanelIndex
]->SetExpanded(true);
247 weld::Container
* pContents
= maPanels
[nPanelIndex
]->GetContents();
248 pContents
->child_grab_focus();
251 void FocusManager::FocusButton (const sal_Int32 nButtonIndex
)
253 maButtons
[nButtonIndex
]->grab_focus();
256 void FocusManager::MoveFocusInsidePanel (
257 const FocusLocation
& rFocusLocation
,
258 const sal_Int32 nDirection
)
260 const bool bHasToolBoxItem (
261 maPanels
[rFocusLocation
.mnIndex
]->GetTitleBar()->GetToolBox().get_n_items() > 0);
262 switch (rFocusLocation
.meComponent
)
265 if (nDirection
> 0 && bHasToolBoxItem
)
266 maPanels
[rFocusLocation
.mnIndex
]->GetTitleBar()->GetToolBox().grab_focus();
268 FocusPanelContent(rFocusLocation
.mnIndex
);
271 case PC_PanelToolBox
:
272 if (nDirection
< 0 && bHasToolBoxItem
)
273 maPanels
[rFocusLocation
.mnIndex
]->GetTitleBar()->GetExpander().grab_focus();
275 FocusPanelContent(rFocusLocation
.mnIndex
);
282 bool FocusManager::MoveFocusInsideDeckTitle (
283 const FocusLocation
& rFocusLocation
,
284 const sal_Int32 nDirection
)
286 bool bConsumed
= false;
287 // Note that when the title bar of the first (and only) panel is
288 // not visible then the deck title takes its place and the focus
289 // is moved between a) deck closer and b) content of panel 0.
290 switch (rFocusLocation
.meComponent
)
293 if (nDirection
>0 && ! IsPanelTitleVisible(0))
295 FocusPanelContent(0);
305 bool FocusManager::HandleKeyEvent(
306 const vcl::KeyCode
& rKeyCode
,
307 const FocusLocation
& aLocation
)
309 bool bConsumed
= false;
311 switch (rKeyCode
.GetCode())
314 switch (aLocation
.meComponent
)
319 case PC_PanelToolBox
:
323 mxDeck
->GrabFocusToDocument();
328 case PC_PanelContent
:
329 // Return focus to tab bar sidebar settings button or panel title.
330 if (!IsDeckTitleVisible() && maPanels
.size() == 1)
333 FocusPanel(aLocation
.mnIndex
, true);
342 switch (aLocation
.meComponent
)
351 FocusPanelContent(aLocation
.mnIndex
);
362 const sal_Int32
nDirection (
366 switch (aLocation
.meComponent
)
369 case PC_PanelToolBox
:
370 MoveFocusInsidePanel(aLocation
, nDirection
);
375 bConsumed
= MoveFocusInsideDeckTitle(aLocation
, nDirection
);
379 if (rKeyCode
.IsShift())
380 FocusPanel(maPanels
.size()-1, true);
383 if (IsDeckTitleVisible())
399 switch (aLocation
.meComponent
)
402 case PC_PanelToolBox
:
403 // Go to previous panel or the deck title.
404 if (aLocation
.mnIndex
> 0)
405 FocusPanel(aLocation
.mnIndex
-1, true);
406 else if (IsDeckTitleVisible())
410 // Focus the last button.
411 sal_Int32
nIndex(maButtons
.size()-1);
412 while(!maButtons
[nIndex
]->get_visible() && --nIndex
> 0);
420 // Focus the last button.
421 sal_Int32
nIndex(maButtons
.size()-1);
422 while(!maButtons
[nIndex
]->get_visible() && --nIndex
> 0);
429 // Go to previous tab bar item.
430 if (aLocation
.mnIndex
== 0)
431 FocusPanel(maPanels
.size()-1, true);
434 sal_Int32
nIndex((aLocation
.mnIndex
+ maButtons
.size() - 1) % maButtons
.size());
435 while(!maButtons
[nIndex
]->get_visible() && --nIndex
> 0);
448 switch(aLocation
.meComponent
)
451 case PC_PanelToolBox
:
453 if (aLocation
.mnIndex
< static_cast<sal_Int32
>(maPanels
.size())-1)
454 FocusPanel(aLocation
.mnIndex
+1, false);
461 // Focus the first panel.
462 if (IsPanelTitleVisible(0))
463 FocusPanel(0, false);
470 // Go to next tab bar item.
471 if (aLocation
.mnIndex
< static_cast<sal_Int32
>(maButtons
.size())-1)
473 sal_Int32
nIndex(aLocation
.mnIndex
+ 1);
474 while(!maButtons
[nIndex
]->get_visible() && ++nIndex
< static_cast<sal_Int32
>(maButtons
.size()));
475 if (nIndex
< static_cast<sal_Int32
>(maButtons
.size()))
482 if (IsDeckTitleVisible())
497 IMPL_LINK(FocusManager
, KeyInputHdl
, const KeyEvent
&, rKeyEvent
, bool)
499 return HandleKeyEvent(rKeyEvent
.GetKeyCode(), GetFocusLocation());
502 } // end of namespace sfx2::sidebar
504 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */