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/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
),
37 FocusManager::FocusManager(const std::function
<void(const Panel
&)>& rShowPanelFunctor
)
41 maShowPanelFunctor(rShowPanelFunctor
)
45 FocusManager::~FocusManager()
50 void FocusManager::GrabFocus()
55 void FocusManager::GrabFocusPanel()
60 void FocusManager::Clear()
62 SetDeckTitle(nullptr);
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
)
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
)
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();
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();
194 FocusPanel(0, false);
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()))
210 VclPtr
<TitleBar
> pTitleBar
= maPanels
[nPanelIndex
]->GetTitleBar();
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
)
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
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
241 if (IsDeckTitleVisible())
244 FocusPanelContent(nPanelIndex
);
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());
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
);
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
)
292 if (nDirection
> 0 && bHasToolBoxItem
)
293 maPanels
[rFocusLocation
.mnIndex
]->GetTitleBar()->GetToolBox().grab_focus();
295 FocusPanelContent(rFocusLocation
.mnIndex
);
298 case PC_PanelToolBox
:
299 if (nDirection
< 0 && bHasToolBoxItem
)
300 maPanels
[rFocusLocation
.mnIndex
]->GetTitleBar()->GetExpander().grab_focus();
302 FocusPanelContent(rFocusLocation
.mnIndex
);
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
)
319 if (nDirection
>0 && ! IsPanelTitleVisible(0))
320 FocusPanelContent(0);
327 bool FocusManager::HandleKeyEvent(
328 const vcl::KeyCode
& rKeyCode
,
329 const FocusLocation
& aLocation
)
331 bool bConsumed
= false;
333 switch (rKeyCode
.GetCode())
336 switch (aLocation
.meComponent
)
341 case PC_PanelToolBox
:
343 vcl::Window
* pFocusWin
= Application::GetFocusWindow();
346 pFocusWin
->GrabFocusToDocument();
358 switch (aLocation
.meComponent
)
367 FocusPanelContent(aLocation
.mnIndex
);
378 const sal_Int32
nDirection (
382 switch (aLocation
.meComponent
)
385 case PC_PanelToolBox
:
386 case PC_PanelContent
:
387 MoveFocusInsidePanel(aLocation
, nDirection
);
392 MoveFocusInsideDeckTitle(aLocation
, nDirection
);
404 switch (aLocation
.meComponent
)
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())
416 // Focus the last button.
417 sal_Int32
nIndex(maButtons
.size()-1);
418 while(!maButtons
[nIndex
]->get_visible() && --nIndex
> 0);
426 // Focus the last button.
427 sal_Int32
nIndex(maButtons
.size()-1);
428 while(!maButtons
[nIndex
]->get_visible() && --nIndex
> 0);
435 // Go to previous tab bar item.
436 if (aLocation
.mnIndex
== 0)
437 FocusPanel(maPanels
.size()-1, true);
440 sal_Int32
nIndex((aLocation
.mnIndex
+ maButtons
.size() - 1) % maButtons
.size());
441 while(!maButtons
[nIndex
]->get_visible() && --nIndex
> 0);
454 switch(aLocation
.meComponent
)
457 case PC_PanelToolBox
:
458 case PC_PanelContent
:
460 if (aLocation
.mnIndex
< static_cast<sal_Int32
>(maPanels
.size())-1)
461 FocusPanel(aLocation
.mnIndex
+1, false);
468 // Focus the first panel.
469 if (IsPanelTitleVisible(0))
470 FocusPanel(0, false);
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()))
489 if (IsDeckTitleVisible())
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)
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);
527 if (pWindow
== nullptr)
529 aLocation
= GetFocusLocation(*pWindow
);
530 if (aLocation
.meComponent
!= PC_None
)
532 pWindow
= pWindow
->GetParent();
535 if (aLocation
.meComponent
!= PC_None
)
537 switch (pKeyEvent
->GetKeyCode().GetCode())
540 // Return focus to tab bar sidebar settings button or panel title.
541 if (!IsDeckTitleVisible() && maPanels
.size() == 1)
544 FocusPanel(aLocation
.mnIndex
, true);
559 } // end of namespace sfx2::sidebar
561 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */