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/Deck.hxx>
21 #include <sidebar/DeckDescriptor.hxx>
22 #include <sidebar/DeckLayouter.hxx>
23 #include <sidebar/DrawHelper.hxx>
24 #include <sidebar/DeckTitleBar.hxx>
25 #include <sidebar/PanelTitleBar.hxx>
26 #include <sfx2/sidebar/Panel.hxx>
27 #include <sfx2/sidebar/Theme.hxx>
28 #include <sfx2/lokhelper.hxx>
30 #include <vcl/event.hxx>
31 #include <comphelper/lok.hxx>
32 #include <vcl/scrbar.hxx>
33 #include <vcl/commandevent.hxx>
34 #include <vcl/IDialogRenderable.hxx>
35 #include <toolkit/helper/vclunohelper.hxx>
36 #include <tools/svborder.hxx>
37 #include <tools/json_writer.hxx>
38 #include <sal/log.hxx>
41 using namespace css::uno
;
43 namespace sfx2::sidebar
{
45 Deck::Deck(const DeckDescriptor
& rDeckDescriptor
, vcl::Window
* pParentWindow
,
46 const std::function
<void()>& rCloserAction
)
47 : Window(pParentWindow
, 0)
48 , msId(rDeckDescriptor
.msId
)
52 , mpTitleBar(VclPtr
<DeckTitleBar
>::Create(rDeckDescriptor
.msTitle
, this, rCloserAction
))
53 , mpScrollClipWindow(VclPtr
<vcl::Window
>::Create(this))
54 , mpScrollContainer(VclPtr
<ScrollContainerWindow
>::Create(mpScrollClipWindow
.get()))
55 , mpFiller(VclPtr
<vcl::Window
>::Create(this))
56 , mpVerticalScrollBar(VclPtr
<ScrollBar
>::Create(this))
58 mpScrollClipWindow
->SetBackground(Wallpaper());
59 mpScrollClipWindow
->Show();
61 mpScrollContainer
->SetStyle(mpScrollContainer
->GetStyle() | WB_DIALOGCONTROL
);
62 mpScrollContainer
->SetBackground(Wallpaper());
63 mpScrollContainer
->Show();
65 mpVerticalScrollBar
->SetScrollHdl(LINK(this, Deck
, HandleVerticalScrollBarChange
));
66 mpVerticalScrollBar
->SetLineSize(10);
67 mpVerticalScrollBar
->SetPageSize(100);
70 SetText(OUString("Deck"));
71 mpScrollClipWindow
->SetText(OUString("ScrollClipWindow"));
72 mpFiller
->SetText(OUString("Filler"));
73 mpVerticalScrollBar
->SetText(OUString("VerticalScrollBar"));
84 SharedPanelContainer aPanels
;
85 aPanels
.swap(maPanels
);
87 // We have to explicitly trigger the destruction of panels.
88 // Otherwise that is done by one of our base class destructors
89 // without updating maPanels.
90 for (VclPtr
<Panel
> & rpPanel
: aPanels
)
91 rpPanel
.disposeAndClear();
93 maPanels
.clear(); // just to keep the loplugin:vclwidgets happy
94 mpTitleBar
.disposeAndClear();
95 mpFiller
.disposeAndClear();
96 mpVerticalScrollBar
.disposeAndClear();
97 mpScrollContainer
.disposeAndClear();
98 mpScrollClipWindow
.disposeAndClear();
100 vcl::Window::dispose();
103 VclPtr
<DeckTitleBar
> const & Deck::GetTitleBar() const
108 tools::Rectangle
Deck::GetContentArea() const
110 const Size
aWindowSize (GetSizePixel());
111 const int nBorderSize (Theme::GetInteger(Theme::Int_DeckBorderSize
));
112 if (aWindowSize
.IsEmpty())
113 return tools::Rectangle();
115 return tools::Rectangle(
116 Theme::GetInteger(Theme::Int_DeckLeftPadding
) + nBorderSize
,
117 Theme::GetInteger(Theme::Int_DeckTopPadding
) + nBorderSize
,
118 aWindowSize
.Width() - 1 - Theme::GetInteger(Theme::Int_DeckRightPadding
) - nBorderSize
,
119 aWindowSize
.Height() - 1 - Theme::GetInteger(Theme::Int_DeckBottomPadding
) - nBorderSize
);
122 void Deck::ApplySettings(vcl::RenderContext
& rRenderContext
)
124 rRenderContext
.SetBackground(Wallpaper());
127 void Deck::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& /*rUpdateArea*/)
129 const Size
aWindowSize (GetSizePixel());
130 const SvBorder
aPadding(Theme::GetInteger(Theme::Int_DeckLeftPadding
),
131 Theme::GetInteger(Theme::Int_DeckTopPadding
),
132 Theme::GetInteger(Theme::Int_DeckRightPadding
),
133 Theme::GetInteger(Theme::Int_DeckBottomPadding
));
135 // Paint deck background outside the border.
136 tools::Rectangle
aBox(0, 0, aWindowSize
.Width() - 1, aWindowSize
.Height() - 1);
137 DrawHelper::DrawBorder(rRenderContext
, aBox
, aPadding
,
138 Theme::GetColor(Theme::Color_DeckBackground
),
139 Theme::GetColor(Theme::Color_DeckBackground
));
142 const int nBorderSize(Theme::GetInteger(Theme::Int_DeckBorderSize
));
143 aBox
.AdjustLeft(aPadding
.Left() );
144 aBox
.AdjustTop(aPadding
.Top() );
145 aBox
.AdjustRight( -(aPadding
.Right()) );
146 aBox
.AdjustBottom( -(aPadding
.Bottom()) );
147 const Color
nHorizontalBorderPaint(Theme::GetColor(Theme::Color_HorizontalBorder
));
148 DrawHelper::DrawBorder(rRenderContext
, aBox
,
149 SvBorder(nBorderSize
, nBorderSize
, nBorderSize
, nBorderSize
),
150 nHorizontalBorderPaint
,
151 Theme::GetColor(Theme::Color_VerticalBorder
));
154 void Deck::DataChanged (const DataChangedEvent
&)
156 RequestLayoutInternal();
159 bool Deck::EventNotify(NotifyEvent
& rEvent
)
161 if (rEvent
.GetType() == MouseNotifyEvent::COMMAND
)
163 CommandEvent
* pCommandEvent
= static_cast<CommandEvent
*>(rEvent
.GetData());
164 if (pCommandEvent
!= nullptr)
165 switch (pCommandEvent
->GetCommand())
167 case CommandEventId::Wheel
:
168 return ProcessWheelEvent(pCommandEvent
);
175 return Window::EventNotify(rEvent
);
182 if (const vcl::ILibreOfficeKitNotifier
* pNotifier
= GetLOKNotifier())
184 std::vector
<vcl::LOKPayloadItem
> aItems
;
185 aItems
.emplace_back("type", "deck");
186 aItems
.emplace_back(std::make_pair("position", Point(GetOutOffXPixel(), GetOutOffYPixel()).toString()));
187 aItems
.emplace_back(std::make_pair("size", GetSizePixel().toString()));
188 pNotifier
->notifyWindow(GetLOKWindowId(), "size_changed", aItems
);
193 * Get the ordering as is shown in the layout, and our type as 'deck'
194 * also elide nested panel windows.
196 void Deck::DumpAsPropertyTree(tools::JsonWriter
& rJsonWriter
)
198 rJsonWriter
.put("id", get_id()); // TODO could be missing - sort out
199 rJsonWriter
.put("type", "deck");
200 rJsonWriter
.put("text", GetText());
201 rJsonWriter
.put("enabled", IsEnabled());
203 auto childrenNode
= rJsonWriter
.startNode("children");
204 for (auto &it
: maPanels
)
209 // collapse the panel itself out
210 auto xContent
= it
->GetElementWindow();
213 VclPtr
<vcl::Window
> pWindow
= VCLUnoHelper::GetWindow(xContent
);
217 auto childNode
= rJsonWriter
.startNode("");
218 rJsonWriter
.put("id", it
->GetId());
219 rJsonWriter
.put("type", "panel");
220 rJsonWriter
.put("text", it
->GetText());
221 rJsonWriter
.put("enabled", it
->IsEnabled());
223 auto children2Node
= rJsonWriter
.startNode("children");
224 pWindow
->DumpAsPropertyTree(rJsonWriter
);
228 bool Deck::ProcessWheelEvent(CommandEvent
const * pCommandEvent
)
230 if ( ! mpVerticalScrollBar
)
232 if ( ! mpVerticalScrollBar
->IsVisible())
235 // Get the wheel data and check that it describes a valid vertical
237 const CommandWheelData
* pData
= pCommandEvent
->GetWheelData();
239 || pData
->GetModifier()
240 || pData
->GetMode() != CommandWheelMode::SCROLL
244 // Execute the actual scroll action.
245 tools::Long nDelta
= pData
->GetDelta();
246 mpVerticalScrollBar
->DoScroll(
247 mpVerticalScrollBar
->GetThumbPos() - nDelta
);
252 * This container may contain existing panels that are
253 * being re-used, and new ones too.
255 void Deck::ResetPanels(const SharedPanelContainer
& rPanelContainer
)
257 SharedPanelContainer aHiddens
;
259 // First hide old panels we don't need just now.
260 for (VclPtr
<Panel
> & rpPanel
: maPanels
)
263 for (const auto & i
: rPanelContainer
)
264 bFound
= bFound
|| (rpPanel
.get() == i
.get());
265 if (!bFound
) // this one didn't survive.
267 rpPanel
->SetLurkMode(true);
268 aHiddens
.push_back(rpPanel
);
271 maPanels
= rPanelContainer
;
273 // Hidden ones always at the end
274 maPanels
.insert(std::end(maPanels
), std::begin(aHiddens
), std::end(aHiddens
));
276 RequestLayoutInternal();
279 void Deck::RequestLayoutInternal()
284 DeckLayouter::LayoutDeck(GetContentArea(), mnMinimalWidth
, mnMinimalHeight
, maPanels
,
285 *GetTitleBar(), *mpScrollClipWindow
, *mpScrollContainer
,
286 *mpFiller
, *mpVerticalScrollBar
);
289 void Deck::RequestLayout()
291 RequestLayoutInternal();
293 if (!comphelper::LibreOfficeKit::isActive())
296 bool bChangeNeeded
= false;
297 Size aParentSize
= GetParent()->GetSizePixel();
299 if (mnMinimalHeight
> 0 && (mnMinimalHeight
!= aParentSize
.Height() || GetSizePixel().Height() != mnMinimalHeight
))
301 aParentSize
.setHeight(mnMinimalHeight
);
302 bChangeNeeded
= true;
304 const SfxViewShell
* pViewShell
= SfxViewShell::Current();
305 if (mnMinimalWidth
> 0 && (mnMinimalWidth
!= aParentSize
.Width() || GetSizePixel().Width() != mnMinimalWidth
)
306 && pViewShell
&& pViewShell
->isLOKMobilePhone())
308 aParentSize
.setWidth(mnMinimalWidth
);
309 bChangeNeeded
= true;
314 GetParent()->SetSizePixel(aParentSize
);
315 setPosSizePixel(0, 0, aParentSize
.Width(), aParentSize
.Height());
317 else if (aParentSize
!= GetSizePixel()) //Sync parent & child sizes
318 setPosSizePixel(0, 0, aParentSize
.Width(), aParentSize
.Height());
321 vcl::Window
* Deck::GetPanelParentWindow()
323 return mpScrollContainer
.get();
326 Panel
* Deck::GetPanel(const OUString
& panelId
)
328 for (const VclPtr
<Panel
> & pPanel
: maPanels
)
330 if(pPanel
->GetId() == panelId
)
339 void Deck::ShowPanel(const Panel
& rPanel
)
341 if (!mpVerticalScrollBar
|| !mpVerticalScrollBar
->IsVisible())
344 // Get vertical extent of the panel.
345 sal_Int32
nPanelTop (rPanel
.GetPosPixel().Y());
346 const sal_Int32
nPanelBottom (nPanelTop
+ rPanel
.GetSizePixel().Height() - 1);
347 // Add the title bar into the extent.
348 if (rPanel
.GetTitleBar() && rPanel
.GetTitleBar()->IsVisible())
349 nPanelTop
= rPanel
.GetTitleBar()->GetPosPixel().Y();
351 // Determine what the new thumb position should be like.
352 // When the whole panel does not fit then make its top visible
353 // and it off at the bottom.
354 sal_Int32
nNewThumbPos (mpVerticalScrollBar
->GetThumbPos());
355 if (nPanelBottom
>= nNewThumbPos
+mpVerticalScrollBar
->GetVisibleSize())
356 nNewThumbPos
= nPanelBottom
- mpVerticalScrollBar
->GetVisibleSize();
357 if (nPanelTop
< nNewThumbPos
)
358 nNewThumbPos
= nPanelTop
;
360 mpVerticalScrollBar
->SetThumbPos(nNewThumbPos
);
361 mpScrollContainer
->SetPosPixel(
363 mpScrollContainer
->GetPosPixel().X(),
367 static OUString
GetWindowClassification(const vcl::Window
* pWindow
)
369 const OUString
& rsName (pWindow
->GetText());
370 if (!rsName
.isEmpty())
380 void Deck::PrintWindowSubTree(vcl::Window
* pRoot
, int nIndentation
)
382 static const char* const sIndentation
= " ";
383 const Point
aLocation (pRoot
->GetPosPixel());
384 const Size
aSize (pRoot
->GetSizePixel());
387 sIndentation
+ strlen(sIndentation
) - nIndentation
* 4 << pRoot
<< " "
388 << GetWindowClassification(pRoot
) << " "
389 << (pRoot
->IsVisible() ? "visible" : "hidden") << " +"
390 << aLocation
.X() << "+" << aLocation
.Y() << " x" << aSize
.Width()
391 << "x" << aSize
.Height());
393 const sal_uInt16
nChildCount(pRoot
->GetChildCount());
394 for (sal_uInt16 nIndex
= 0; nIndex
< nChildCount
; ++nIndex
)
395 PrintWindowSubTree(pRoot
->GetChild(nIndex
), nIndentation
+ 1);
398 IMPL_LINK_NOARG(Deck
, HandleVerticalScrollBarChange
, ScrollBar
*, void)
400 const sal_Int32
nYOffset (-mpVerticalScrollBar
->GetThumbPos());
401 mpScrollContainer
->SetPosPixel(Point(mpScrollContainer
->GetPosPixel().X(),
403 mpScrollContainer
->Invalidate();
406 //----- Deck::ScrollContainerWindow -------------------------------------------
408 Deck::ScrollContainerWindow::ScrollContainerWindow (vcl::Window
* pParentWindow
)
409 : Window(pParentWindow
),
413 SetText(OUString("ScrollContainerWindow"));
417 void Deck::ScrollContainerWindow::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
& /*rUpdateArea*/)
419 // Paint the separators.
420 const sal_Int32
nSeparatorHeight(Theme::GetInteger(Theme::Int_DeckSeparatorHeight
));
421 const sal_Int32
nLeft(0);
422 const sal_Int32
nRight(GetSizePixel().Width() - 1);
423 const Color
nHorizontalBorderPaint(Theme::GetColor(Theme::Color_HorizontalBorder
));
424 for (auto const& separator
: maSeparators
)
426 DrawHelper::DrawHorizontalLine(rRenderContext
, nLeft
, nRight
, separator
,
427 nSeparatorHeight
, nHorizontalBorderPaint
);
431 void Deck::ScrollContainerWindow::SetSeparators (const ::std::vector
<sal_Int32
>& rSeparators
)
433 maSeparators
= rSeparators
;
437 } // end of namespace sfx2::sidebar
439 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */