use insert function instead of for loop
[LibreOffice.git] / sfx2 / source / sidebar / Deck.cxx
blobd534e74a24932accf1cd26ab0f646556ce5a9172
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/DeckTitleBar.hxx>
24 #include <sidebar/PanelTitleBar.hxx>
25 #include <sfx2/sidebar/Panel.hxx>
26 #include <sfx2/sidebar/SidebarDockingWindow.hxx>
27 #include <sfx2/sidebar/Theme.hxx>
28 #include <sfx2/viewsh.hxx>
30 #include <vcl/event.hxx>
31 #include <comphelper/lok.hxx>
32 #include <vcl/jsdialog/executor.hxx>
33 #include <tools/json_writer.hxx>
35 using namespace css;
37 namespace sfx2::sidebar {
39 void Deck::LOKSendSidebarFullUpdate()
41 if (comphelper::LibreOfficeKit::isActive())
43 sal_uInt64 nShellId = reinterpret_cast<sal_uInt64>(SfxViewShell::Current());
44 jsdialog::SendFullUpdate(OUString::number(nShellId) + "sidebar", u"Panel"_ustr);
48 Deck::Deck(const DeckDescriptor& rDeckDescriptor, SidebarDockingWindow* pParentWindow,
49 const std::function<void()>& rCloserAction)
50 : InterimItemWindow(pParentWindow, u"sfx/ui/deck.ui"_ustr, u"Deck"_ustr)
51 , msId(rDeckDescriptor.msId)
52 , mnMinimalWidth(0)
53 , mnScrolledWindowExtraWidth(0)
54 , mnMinimalHeight(0)
55 , maPanels()
56 , mxParentWindow(pParentWindow)
57 , mxTitleBar(new DeckTitleBar(rDeckDescriptor.msTitle, *m_xBuilder,
58 rDeckDescriptor.msHelpId, rCloserAction))
59 , mxVerticalScrollBar(m_xBuilder->weld_scrolled_window(u"scrolledwindow"_ustr))
60 , mxContents(m_xBuilder->weld_box(u"contents"_ustr))
62 SetStyle(GetStyle() | WB_DIALOGCONTROL);
64 m_xContainer->set_background(Theme::GetColor(Theme::Color_DeckBackground));
66 mxVerticalScrollBar->vadjustment_set_step_increment(10);
67 mxVerticalScrollBar->vadjustment_set_page_increment(100);
69 // tdf#142458 Measure the preferred width of an empty ScrolledWindow
70 // to add to the width of the union of panel widths when calculating
71 // the minimal width of the deck
72 mxVerticalScrollBar->set_hpolicy(VclPolicyType::NEVER);
73 mxVerticalScrollBar->set_vpolicy(VclPolicyType::NEVER);
74 mnScrolledWindowExtraWidth = mxVerticalScrollBar->get_preferred_size().Width();
75 mxVerticalScrollBar->set_hpolicy(VclPolicyType::AUTOMATIC);
76 mxVerticalScrollBar->set_vpolicy(VclPolicyType::AUTOMATIC);
79 Deck::~Deck()
81 disposeOnce();
84 void Deck::dispose()
86 SharedPanelContainer aPanels;
87 aPanels.swap(maPanels);
89 // We have to explicitly trigger the destruction of panels.
90 // Otherwise that is done by one of our base class destructors
91 // without updating maPanels.
92 for (auto& rpPanel : aPanels)
93 rpPanel.reset();
95 maPanels.clear();
96 mxTitleBar.reset();
97 mxContents.reset();
98 mxVerticalScrollBar.reset();
100 mxParentWindow.clear();
102 InterimItemWindow::dispose();
105 DeckTitleBar* Deck::GetTitleBar() const
107 return mxTitleBar.get();
110 tools::Rectangle Deck::GetContentArea() const
112 const Size aWindowSize (GetSizePixel());
113 const int nBorderSize (Theme::GetInteger(Theme::Int_DeckBorderSize));
114 if (aWindowSize.IsEmpty())
115 return tools::Rectangle();
117 return tools::Rectangle(
118 Theme::GetInteger(Theme::Int_DeckLeftPadding) + nBorderSize,
119 Theme::GetInteger(Theme::Int_DeckTopPadding) + nBorderSize,
120 aWindowSize.Width() - 1 - Theme::GetInteger(Theme::Int_DeckRightPadding) - nBorderSize,
121 aWindowSize.Height() - 1 - Theme::GetInteger(Theme::Int_DeckBottomPadding) - nBorderSize);
124 void Deck::DataChanged(const DataChangedEvent&)
126 for (auto& rpPanel : maPanels)
127 rpPanel->DataChanged();
129 RequestLayoutInternal();
130 Deck::LOKSendSidebarFullUpdate();
134 * Get the ordering as is shown in the layout, and our type as 'deck'
135 * also elide nested panel windows.
137 void Deck::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
139 rJsonWriter.put("id", get_id().isEmpty() ? msId : get_id());
140 rJsonWriter.put("type", "deck");
141 rJsonWriter.put("text", GetText());
142 rJsonWriter.put("enabled", IsEnabled());
143 if (!IsVisible())
144 rJsonWriter.put("visible", false);
146 auto childrenNode = rJsonWriter.startArray("children");
147 for (const auto &it : maPanels)
149 // collapse the panel itself out
150 auto xContent = it->GetContents();
151 if (!xContent)
152 continue;
154 auto childNode = rJsonWriter.startStruct();
155 rJsonWriter.put("id", it->GetId());
156 rJsonWriter.put("type", "panel");
157 rJsonWriter.put("text", it->GetTitle());
158 rJsonWriter.put("enabled", true);
159 rJsonWriter.put("hidden", it->IsLurking());
160 rJsonWriter.put("expanded", it->IsExpanded());
162 if (it->GetTitleBar() && !it->GetTitleBar()->GetMoreOptionsCommand().isEmpty())
163 rJsonWriter.put("command", it->GetTitleBar()->GetMoreOptionsCommand());
166 auto children2Node = rJsonWriter.startArray("children");
168 auto child2Node = rJsonWriter.startStruct();
169 xContent->get_property_tree(rJsonWriter);
176 * This container may contain existing panels that are
177 * being re-used, and new ones too.
179 void Deck::ResetPanels(SharedPanelContainer&& rPanelContainer)
181 SharedPanelContainer aHiddens;
183 // First hide old panels we don't need just now.
184 for (auto& rpPanel : maPanels)
186 bool bFound = false;
187 for (const auto & i : rPanelContainer)
188 bFound = bFound || (rpPanel.get() == i.get());
189 if (!bFound) // this one didn't survive.
191 rpPanel->SetLurkMode(true);
192 aHiddens.push_back(rpPanel);
196 bool bDifferent = maPanels.size() != rPanelContainer.size() || aHiddens.size();
197 maPanels = std::move(rPanelContainer);
199 // Hidden ones always at the end
200 maPanels.insert(std::end(maPanels), std::begin(aHiddens), std::end(aHiddens));
202 RequestLayoutInternal();
204 if (bDifferent)
205 Deck::LOKSendSidebarFullUpdate();
208 void Deck::RequestLayoutInternal()
210 mnMinimalWidth = 0;
211 mnMinimalHeight = 0;
213 DeckLayouter::LayoutDeck(mxParentWindow.get(), GetContentArea(),
214 mnMinimalWidth, mnMinimalHeight, maPanels,
215 *GetTitleBar(), *mxVerticalScrollBar);
217 if (mnMinimalWidth)
219 // tdf#142458 at this point mnMinimalWidth contains the width required
220 // by the panels, but extra space may be needed by the scrolledwindow
221 // that will contain the panels
222 mnMinimalWidth += mnScrolledWindowExtraWidth;
226 void Deck::RequestLayout()
228 RequestLayoutInternal();
230 if (!comphelper::LibreOfficeKit::isActive())
231 return;
233 bool bChangeNeeded = false;
234 Size aParentSize = mxParentWindow->GetSizePixel();
236 if (mnMinimalHeight > 0 && (mnMinimalHeight != aParentSize.Height() || GetSizePixel().Height() != mnMinimalHeight))
238 aParentSize.setHeight(mnMinimalHeight);
239 bChangeNeeded = true;
241 const SfxViewShell* pViewShell = SfxViewShell::Current();
242 if (mnMinimalWidth > 0 && (mnMinimalWidth != aParentSize.Width() || GetSizePixel().Width() != mnMinimalWidth)
243 && pViewShell && pViewShell->isLOKMobilePhone())
245 aParentSize.setWidth(mnMinimalWidth);
246 bChangeNeeded = true;
249 if (bChangeNeeded)
251 mxParentWindow->SetSizePixel(aParentSize);
252 setPosSizePixel(0, 0, aParentSize.Width(), aParentSize.Height());
254 else if (aParentSize != GetSizePixel()) //Sync parent & child sizes
255 setPosSizePixel(0, 0, aParentSize.Width(), aParentSize.Height());
258 weld::Widget* Deck::GetPanelParentWindow()
260 return mxContents.get();
263 std::shared_ptr<Panel> Deck::GetPanel(std::u16string_view panelId)
265 for (const auto& pPanel : maPanels)
267 if(pPanel->GetId() == panelId)
269 return pPanel;
272 return nullptr;
276 void Deck::ShowPanel(const Panel& rPanel)
278 if (!mxVerticalScrollBar || mxVerticalScrollBar->get_vpolicy() == VclPolicyType::NEVER)
279 return;
281 // Get vertical extent of the panel.
282 tools::Rectangle aExtents;
283 if (!rPanel.get_extents(aExtents))
284 return;
286 auto nPanelTop = aExtents.Top();
287 auto nPanelBottom = aExtents.Bottom() - 1;
289 // Determine what the new thumb position should be like.
290 // When the whole panel does not fit then make its top visible
291 // and it off at the bottom.
292 sal_Int32 nNewThumbPos(mxVerticalScrollBar->vadjustment_get_value());
293 if (nPanelBottom >= nNewThumbPos + mxVerticalScrollBar->vadjustment_get_page_size())
294 nNewThumbPos = nPanelBottom - mxVerticalScrollBar->vadjustment_get_page_size();
295 if (nPanelTop < nNewThumbPos)
296 nNewThumbPos = nPanelTop;
298 mxVerticalScrollBar->vadjustment_set_value(nNewThumbPos);
301 } // end of namespace sfx2::sidebar
303 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */