Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / sfx2 / source / sidebar / Deck.cxx
blob1cc11cafc37ebabbc86968a86892e427e524dac2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
40 using namespace css;
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)
49 , mnMinimalWidth(0)
50 , mnMinimalHeight(0)
51 , maPanels()
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);
69 #ifdef DEBUG
70 SetText(OUString("Deck"));
71 mpScrollClipWindow->SetText(OUString("ScrollClipWindow"));
72 mpFiller->SetText(OUString("Filler"));
73 mpVerticalScrollBar->SetText(OUString("VerticalScrollBar"));
74 #endif
77 Deck::~Deck()
79 disposeOnce();
82 void Deck::dispose()
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
105 return mpTitleBar;
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));
141 // Paint the border.
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);
170 default:
171 break;
175 return Window::EventNotify(rEvent);
178 void Deck::Resize()
180 Window::Resize();
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)
206 if (it->IsLurking())
207 continue;
209 // collapse the panel itself out
210 auto xContent = it->GetElementWindow();
211 if (!xContent.is())
212 continue;
213 VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xContent);
214 if (!pWindow)
215 continue;
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)
231 return false;
232 if ( ! mpVerticalScrollBar->IsVisible())
233 return false;
235 // Get the wheel data and check that it describes a valid vertical
236 // scroll.
237 const CommandWheelData* pData = pCommandEvent->GetWheelData();
238 if (pData==nullptr
239 || pData->GetModifier()
240 || pData->GetMode() != CommandWheelMode::SCROLL
241 || pData->IsHorz())
242 return false;
244 // Execute the actual scroll action.
245 tools::Long nDelta = pData->GetDelta();
246 mpVerticalScrollBar->DoScroll(
247 mpVerticalScrollBar->GetThumbPos() - nDelta);
248 return true;
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)
262 bool bFound = false;
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()
281 mnMinimalWidth = 0;
282 mnMinimalHeight = 0;
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())
294 return;
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;
312 if (bChangeNeeded)
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)
332 return pPanel.get();
335 return nullptr;
339 void Deck::ShowPanel(const Panel& rPanel)
341 if (!mpVerticalScrollBar || !mpVerticalScrollBar->IsVisible())
342 return;
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(
362 Point(
363 mpScrollContainer->GetPosPixel().X(),
364 -nNewThumbPos));
367 static OUString GetWindowClassification(const vcl::Window* pWindow)
369 const OUString& rsName (pWindow->GetText());
370 if (!rsName.isEmpty())
372 return rsName;
374 else
376 return "window";
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());
385 SAL_INFO(
386 "sfx.sidebar",
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(),
402 nYOffset));
403 mpScrollContainer->Invalidate();
406 //----- Deck::ScrollContainerWindow -------------------------------------------
408 Deck::ScrollContainerWindow::ScrollContainerWindow (vcl::Window* pParentWindow)
409 : Window(pParentWindow),
410 maSeparators()
412 #ifdef DEBUG
413 SetText(OUString("ScrollContainerWindow"));
414 #endif
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;
434 Invalidate();
437 } // end of namespace sfx2::sidebar
439 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */