/src/mozilla-central/layout/xul/nsSplitterFrame.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | // |
8 | | // Eric Vaughan |
9 | | // Netscape Communications |
10 | | // |
11 | | // See documentation in associated header file |
12 | | // |
13 | | |
14 | | #include "gfxContext.h" |
15 | | #include "nsSplitterFrame.h" |
16 | | #include "nsGkAtoms.h" |
17 | | #include "nsXULElement.h" |
18 | | #include "nsPresContext.h" |
19 | | #include "nsIDocument.h" |
20 | | #include "nsNameSpaceManager.h" |
21 | | #include "nsScrollbarButtonFrame.h" |
22 | | #include "nsIDOMEventListener.h" |
23 | | #include "nsIPresShell.h" |
24 | | #include "nsFrameList.h" |
25 | | #include "nsHTMLParts.h" |
26 | | #include "mozilla/ComputedStyle.h" |
27 | | #include "nsBoxLayoutState.h" |
28 | | #include "nsIServiceManager.h" |
29 | | #include "nsContainerFrame.h" |
30 | | #include "nsContentCID.h" |
31 | | #include "nsLayoutUtils.h" |
32 | | #include "nsDisplayList.h" |
33 | | #include "nsContentUtils.h" |
34 | | #include "mozilla/dom/Element.h" |
35 | | #include "mozilla/dom/Event.h" |
36 | | #include "mozilla/dom/MouseEvent.h" |
37 | | #include "mozilla/MouseEvents.h" |
38 | | #include "mozilla/UniquePtr.h" |
39 | | #include "nsBindingManager.h" |
40 | | |
41 | | using namespace mozilla; |
42 | | |
43 | | class nsSplitterInfo { |
44 | | public: |
45 | | nscoord min; |
46 | | nscoord max; |
47 | | nscoord current; |
48 | | nscoord changed; |
49 | | nsCOMPtr<nsIContent> childElem; |
50 | | int32_t flex; |
51 | | int32_t index; |
52 | | }; |
53 | | |
54 | | class nsSplitterFrameInner final : public nsIDOMEventListener |
55 | | { |
56 | | protected: |
57 | | virtual ~nsSplitterFrameInner(); |
58 | | |
59 | | public: |
60 | | |
61 | | NS_DECL_ISUPPORTS |
62 | | NS_DECL_NSIDOMEVENTLISTENER |
63 | | |
64 | | explicit nsSplitterFrameInner(nsSplitterFrame* aSplitter) |
65 | | : mDidDrag(false) |
66 | | , mDragStart(0) |
67 | | , mParentBox(nullptr) |
68 | | , mChildInfosBeforeCount(0) |
69 | | , mChildInfosAfterCount(0) |
70 | | , mState(Open) |
71 | | , mSplitterPos(0) |
72 | | , mDragging(false) |
73 | 0 | { |
74 | 0 | mOuter = aSplitter; |
75 | 0 | mPressed = false; |
76 | 0 | } |
77 | | |
78 | 0 | void Disconnect() { mOuter = nullptr; } |
79 | | |
80 | | nsresult MouseDown(Event* aMouseEvent); |
81 | | nsresult MouseUp(Event* aMouseEvent); |
82 | | nsresult MouseMove(Event* aMouseEvent); |
83 | | |
84 | | void MouseDrag(nsPresContext* aPresContext, WidgetGUIEvent* aEvent); |
85 | | void MouseUp(nsPresContext* aPresContext, WidgetGUIEvent* aEvent); |
86 | | |
87 | | void AdjustChildren(nsPresContext* aPresContext); |
88 | | void AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal); |
89 | | |
90 | | void AddRemoveSpace(nscoord aDiff, |
91 | | nsSplitterInfo* aChildInfos, |
92 | | int32_t aCount, |
93 | | int32_t& aSpaceLeft); |
94 | | |
95 | | void ResizeChildTo(nscoord& aDiff, |
96 | | nsSplitterInfo* aChildrenBeforeInfos, |
97 | | nsSplitterInfo* aChildrenAfterInfos, |
98 | | int32_t aChildrenBeforeCount, |
99 | | int32_t aChildrenAfterCount, |
100 | | bool aBounded); |
101 | | |
102 | | void UpdateState(); |
103 | | |
104 | | void AddListener(); |
105 | | void RemoveListener(); |
106 | | |
107 | | enum ResizeType { Closest, Farthest, Flex, Grow }; |
108 | | enum State { Open, CollapsedBefore, CollapsedAfter, Dragging }; |
109 | | enum CollapseDirection { Before, After }; |
110 | | |
111 | | ResizeType GetResizeBefore(); |
112 | | ResizeType GetResizeAfter(); |
113 | | State GetState(); |
114 | | |
115 | | void Reverse(UniquePtr<nsSplitterInfo[]>& aIndexes, int32_t aCount); |
116 | | bool SupportsCollapseDirection(CollapseDirection aDirection); |
117 | | |
118 | | void EnsureOrient(); |
119 | | void SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize); |
120 | | |
121 | | nsSplitterFrame* mOuter; |
122 | | bool mDidDrag; |
123 | | nscoord mDragStart; |
124 | | nsIFrame* mParentBox; |
125 | | bool mPressed; |
126 | | UniquePtr<nsSplitterInfo[]> mChildInfosBefore; |
127 | | UniquePtr<nsSplitterInfo[]> mChildInfosAfter; |
128 | | int32_t mChildInfosBeforeCount; |
129 | | int32_t mChildInfosAfterCount; |
130 | | State mState; |
131 | | nscoord mSplitterPos; |
132 | | bool mDragging; |
133 | | |
134 | 0 | const Element* SplitterElement() const { |
135 | 0 | return mOuter->GetContent()->AsElement(); |
136 | 0 | } |
137 | | }; |
138 | | |
139 | | NS_IMPL_ISUPPORTS(nsSplitterFrameInner, nsIDOMEventListener) |
140 | | |
141 | | nsSplitterFrameInner::ResizeType |
142 | | nsSplitterFrameInner::GetResizeBefore() |
143 | 0 | { |
144 | 0 | static Element::AttrValuesArray strings[] = |
145 | 0 | {&nsGkAtoms::farthest, &nsGkAtoms::flex, nullptr}; |
146 | 0 | switch (SplitterElement()->FindAttrValueIn(kNameSpaceID_None, |
147 | 0 | nsGkAtoms::resizebefore, |
148 | 0 | strings, eCaseMatters)) { |
149 | 0 | case 0: return Farthest; |
150 | 0 | case 1: return Flex; |
151 | 0 | } |
152 | 0 | return Closest; |
153 | 0 | } |
154 | | |
155 | | nsSplitterFrameInner::~nsSplitterFrameInner() |
156 | 0 | { |
157 | 0 | } |
158 | | |
159 | | nsSplitterFrameInner::ResizeType |
160 | | nsSplitterFrameInner::GetResizeAfter() |
161 | 0 | { |
162 | 0 | static Element::AttrValuesArray strings[] = |
163 | 0 | {&nsGkAtoms::farthest, &nsGkAtoms::flex, &nsGkAtoms::grow, nullptr}; |
164 | 0 | switch (SplitterElement()->FindAttrValueIn(kNameSpaceID_None, |
165 | 0 | nsGkAtoms::resizeafter, |
166 | 0 | strings, eCaseMatters)) { |
167 | 0 | case 0: return Farthest; |
168 | 0 | case 1: return Flex; |
169 | 0 | case 2: return Grow; |
170 | 0 | } |
171 | 0 | return Closest; |
172 | 0 | } |
173 | | |
174 | | nsSplitterFrameInner::State |
175 | | nsSplitterFrameInner::GetState() |
176 | 0 | { |
177 | 0 | static Element::AttrValuesArray strings[] = |
178 | 0 | {&nsGkAtoms::dragging, &nsGkAtoms::collapsed, nullptr}; |
179 | 0 | static Element::AttrValuesArray strings_substate[] = |
180 | 0 | {&nsGkAtoms::before, &nsGkAtoms::after, nullptr}; |
181 | 0 | switch (SplitterElement()->FindAttrValueIn(kNameSpaceID_None, |
182 | 0 | nsGkAtoms::state, |
183 | 0 | strings, eCaseMatters)) { |
184 | 0 | case 0: return Dragging; |
185 | 0 | case 1: |
186 | 0 | switch (SplitterElement()->FindAttrValueIn(kNameSpaceID_None, |
187 | 0 | nsGkAtoms::substate, |
188 | 0 | strings_substate, |
189 | 0 | eCaseMatters)) { |
190 | 0 | case 0: return CollapsedBefore; |
191 | 0 | case 1: return CollapsedAfter; |
192 | 0 | default: |
193 | 0 | if (SupportsCollapseDirection(After)) |
194 | 0 | return CollapsedAfter; |
195 | 0 | return CollapsedBefore; |
196 | 0 | } |
197 | 0 | } |
198 | 0 | return Open; |
199 | 0 | } |
200 | | |
201 | | // |
202 | | // NS_NewSplitterFrame |
203 | | // |
204 | | // Creates a new Toolbar frame and returns it |
205 | | // |
206 | | nsIFrame* |
207 | | NS_NewSplitterFrame (nsIPresShell* aPresShell, ComputedStyle* aStyle) |
208 | 0 | { |
209 | 0 | return new (aPresShell) nsSplitterFrame(aStyle); |
210 | 0 | } |
211 | | |
212 | | NS_IMPL_FRAMEARENA_HELPERS(nsSplitterFrame) |
213 | | |
214 | | nsSplitterFrame::nsSplitterFrame(ComputedStyle* aStyle) |
215 | | : nsBoxFrame(aStyle, kClassID), |
216 | | mInner(0) |
217 | 0 | { |
218 | 0 | } |
219 | | |
220 | | void |
221 | | nsSplitterFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) |
222 | 0 | { |
223 | 0 | if (mInner) { |
224 | 0 | mInner->RemoveListener(); |
225 | 0 | mInner->Disconnect(); |
226 | 0 | mInner->Release(); |
227 | 0 | mInner = nullptr; |
228 | 0 | } |
229 | 0 | nsBoxFrame::DestroyFrom(aDestructRoot, aPostDestroyData); |
230 | 0 | } |
231 | | |
232 | | |
233 | | nsresult |
234 | | nsSplitterFrame::GetCursor(const nsPoint& aPoint, |
235 | | nsIFrame::Cursor& aCursor) |
236 | 0 | { |
237 | 0 | return nsBoxFrame::GetCursor(aPoint, aCursor); |
238 | 0 |
|
239 | 0 | /* |
240 | 0 | if (IsXULHorizontal()) |
241 | 0 | aCursor = NS_STYLE_CURSOR_N_RESIZE; |
242 | 0 | else |
243 | 0 | aCursor = NS_STYLE_CURSOR_W_RESIZE; |
244 | 0 |
|
245 | 0 | return NS_OK; |
246 | 0 | */ |
247 | 0 | } |
248 | | |
249 | | nsresult |
250 | | nsSplitterFrame::AttributeChanged(int32_t aNameSpaceID, |
251 | | nsAtom* aAttribute, |
252 | | int32_t aModType) |
253 | 0 | { |
254 | 0 | nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, |
255 | 0 | aModType); |
256 | 0 | if (aAttribute == nsGkAtoms::state) { |
257 | 0 | mInner->UpdateState(); |
258 | 0 | } |
259 | 0 |
|
260 | 0 | return rv; |
261 | 0 | } |
262 | | |
263 | | /** |
264 | | * Initialize us. If we are in a box get our alignment so we know what direction we are |
265 | | */ |
266 | | void |
267 | | nsSplitterFrame::Init(nsIContent* aContent, |
268 | | nsContainerFrame* aParent, |
269 | | nsIFrame* aPrevInFlow) |
270 | 0 | { |
271 | 0 | MOZ_ASSERT(!mInner); |
272 | 0 | mInner = new nsSplitterFrameInner(this); |
273 | 0 |
|
274 | 0 | mInner->AddRef(); |
275 | 0 |
|
276 | 0 | // determine orientation of parent, and if vertical, set orient to vertical |
277 | 0 | // on splitter content, then re-resolve style |
278 | 0 | // XXXbz this is pretty messed up, since this can change whether we should |
279 | 0 | // have a frame at all. This really needs a better solution. |
280 | 0 | if (aParent && aParent->IsXULBoxFrame()) { |
281 | 0 | if (!aParent->IsXULHorizontal()) { |
282 | 0 | if (!nsContentUtils::HasNonEmptyAttr(aContent, kNameSpaceID_None, |
283 | 0 | nsGkAtoms::orient)) { |
284 | 0 | aContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::orient, |
285 | 0 | NS_LITERAL_STRING("vertical"), false); |
286 | 0 | } |
287 | 0 | } |
288 | 0 | } |
289 | 0 |
|
290 | 0 | nsBoxFrame::Init(aContent, aParent, aPrevInFlow); |
291 | 0 |
|
292 | 0 | mInner->mState = nsSplitterFrameInner::Open; |
293 | 0 | mInner->AddListener(); |
294 | 0 | mInner->mParentBox = nullptr; |
295 | 0 | } |
296 | | |
297 | | NS_IMETHODIMP |
298 | | nsSplitterFrame::DoXULLayout(nsBoxLayoutState& aState) |
299 | 0 | { |
300 | 0 | if (GetStateBits() & NS_FRAME_FIRST_REFLOW) |
301 | 0 | { |
302 | 0 | mInner->mParentBox = nsBox::GetParentXULBox(this); |
303 | 0 | mInner->UpdateState(); |
304 | 0 | } |
305 | 0 |
|
306 | 0 | return nsBoxFrame::DoXULLayout(aState); |
307 | 0 | } |
308 | | |
309 | | |
310 | | void |
311 | | nsSplitterFrame::GetInitialOrientation(bool& aIsHorizontal) |
312 | 0 | { |
313 | 0 | nsIFrame* box = nsBox::GetParentXULBox(this); |
314 | 0 | if (box) { |
315 | 0 | aIsHorizontal = !box->IsXULHorizontal(); |
316 | 0 | } |
317 | 0 | else |
318 | 0 | nsBoxFrame::GetInitialOrientation(aIsHorizontal); |
319 | 0 | } |
320 | | |
321 | | NS_IMETHODIMP |
322 | | nsSplitterFrame::HandlePress(nsPresContext* aPresContext, |
323 | | WidgetGUIEvent* aEvent, |
324 | | nsEventStatus* aEventStatus) |
325 | 0 | { |
326 | 0 | return NS_OK; |
327 | 0 | } |
328 | | |
329 | | NS_IMETHODIMP |
330 | | nsSplitterFrame::HandleMultiplePress(nsPresContext* aPresContext, |
331 | | WidgetGUIEvent* aEvent, |
332 | | nsEventStatus* aEventStatus, |
333 | | bool aControlHeld) |
334 | 0 | { |
335 | 0 | return NS_OK; |
336 | 0 | } |
337 | | |
338 | | NS_IMETHODIMP |
339 | | nsSplitterFrame::HandleDrag(nsPresContext* aPresContext, |
340 | | WidgetGUIEvent* aEvent, |
341 | | nsEventStatus* aEventStatus) |
342 | 0 | { |
343 | 0 | return NS_OK; |
344 | 0 | } |
345 | | |
346 | | NS_IMETHODIMP |
347 | | nsSplitterFrame::HandleRelease(nsPresContext* aPresContext, |
348 | | WidgetGUIEvent* aEvent, |
349 | | nsEventStatus* aEventStatus) |
350 | 0 | { |
351 | 0 | return NS_OK; |
352 | 0 | } |
353 | | |
354 | | void |
355 | | nsSplitterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
356 | | const nsDisplayListSet& aLists) |
357 | 0 | { |
358 | 0 | nsBoxFrame::BuildDisplayList(aBuilder, aLists); |
359 | 0 |
|
360 | 0 | // if the mouse is captured always return us as the frame. |
361 | 0 | if (mInner->mDragging && aBuilder->IsForEventDelivery()) |
362 | 0 | { |
363 | 0 | // XXX It's probably better not to check visibility here, right? |
364 | 0 | aLists.Outlines()->AppendToTop( |
365 | 0 | MakeDisplayItem<nsDisplayEventReceiver>(aBuilder, this)); |
366 | 0 | return; |
367 | 0 | } |
368 | 0 | } |
369 | | |
370 | | nsresult |
371 | | nsSplitterFrame::HandleEvent(nsPresContext* aPresContext, |
372 | | WidgetGUIEvent* aEvent, |
373 | | nsEventStatus* aEventStatus) |
374 | 0 | { |
375 | 0 | NS_ENSURE_ARG_POINTER(aEventStatus); |
376 | 0 | if (nsEventStatus_eConsumeNoDefault == *aEventStatus) { |
377 | 0 | return NS_OK; |
378 | 0 | } |
379 | 0 | |
380 | 0 | AutoWeakFrame weakFrame(this); |
381 | 0 | RefPtr<nsSplitterFrameInner> inner(mInner); |
382 | 0 | switch (aEvent->mMessage) { |
383 | 0 | case eMouseMove: |
384 | 0 | inner->MouseDrag(aPresContext, aEvent); |
385 | 0 | break; |
386 | 0 |
|
387 | 0 | case eMouseUp: |
388 | 0 | if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) { |
389 | 0 | inner->MouseUp(aPresContext, aEvent); |
390 | 0 | } |
391 | 0 | break; |
392 | 0 |
|
393 | 0 | default: |
394 | 0 | break; |
395 | 0 | } |
396 | 0 | |
397 | 0 | NS_ENSURE_STATE(weakFrame.IsAlive()); |
398 | 0 | return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus); |
399 | 0 | } |
400 | | |
401 | | void |
402 | | nsSplitterFrameInner::MouseUp(nsPresContext* aPresContext, |
403 | | WidgetGUIEvent* aEvent) |
404 | 0 | { |
405 | 0 | if (mDragging && mOuter) { |
406 | 0 | AdjustChildren(aPresContext); |
407 | 0 | AddListener(); |
408 | 0 | nsIPresShell::SetCapturingContent(nullptr, 0); // XXXndeakin is this needed? |
409 | 0 | mDragging = false; |
410 | 0 | State newState = GetState(); |
411 | 0 | // if the state is dragging then make it Open. |
412 | 0 | if (newState == Dragging) { |
413 | 0 | mOuter->mContent->AsElement()->SetAttr(kNameSpaceID_None, |
414 | 0 | nsGkAtoms::state, EmptyString(), |
415 | 0 | true); |
416 | 0 | } |
417 | 0 |
|
418 | 0 | mPressed = false; |
419 | 0 |
|
420 | 0 | // if we dragged then fire a command event. |
421 | 0 | if (mDidDrag) { |
422 | 0 | RefPtr<nsXULElement> element = |
423 | 0 | nsXULElement::FromNode(mOuter->GetContent()); |
424 | 0 | element->DoCommand(); |
425 | 0 | } |
426 | 0 |
|
427 | 0 | //printf("MouseUp\n"); |
428 | 0 | } |
429 | 0 |
|
430 | 0 | mChildInfosBefore = nullptr; |
431 | 0 | mChildInfosAfter = nullptr; |
432 | 0 | mChildInfosBeforeCount = 0; |
433 | 0 | mChildInfosAfterCount = 0; |
434 | 0 | } |
435 | | |
436 | | void |
437 | | nsSplitterFrameInner::MouseDrag(nsPresContext* aPresContext, |
438 | | WidgetGUIEvent* aEvent) |
439 | 0 | { |
440 | 0 | if (mDragging && mOuter) { |
441 | 0 |
|
442 | 0 | //printf("Dragging\n"); |
443 | 0 |
|
444 | 0 | bool isHorizontal = !mOuter->IsXULHorizontal(); |
445 | 0 | // convert coord to pixels |
446 | 0 | nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, |
447 | 0 | mParentBox); |
448 | 0 | nscoord pos = isHorizontal ? pt.x : pt.y; |
449 | 0 |
|
450 | 0 | // mDragStart is in frame coordinates |
451 | 0 | nscoord start = mDragStart; |
452 | 0 |
|
453 | 0 | // take our current position and subtract the start location |
454 | 0 | pos -= start; |
455 | 0 |
|
456 | 0 | //printf("Diff=%d\n", pos); |
457 | 0 |
|
458 | 0 | ResizeType resizeAfter = GetResizeAfter(); |
459 | 0 |
|
460 | 0 | bool bounded; |
461 | 0 |
|
462 | 0 | if (resizeAfter == nsSplitterFrameInner::Grow) |
463 | 0 | bounded = false; |
464 | 0 | else |
465 | 0 | bounded = true; |
466 | 0 |
|
467 | 0 | int i; |
468 | 0 | for (i=0; i < mChildInfosBeforeCount; i++) |
469 | 0 | mChildInfosBefore[i].changed = mChildInfosBefore[i].current; |
470 | 0 |
|
471 | 0 | for (i=0; i < mChildInfosAfterCount; i++) |
472 | 0 | mChildInfosAfter[i].changed = mChildInfosAfter[i].current; |
473 | 0 |
|
474 | 0 | nscoord oldPos = pos; |
475 | 0 |
|
476 | 0 | ResizeChildTo(pos, |
477 | 0 | mChildInfosBefore.get(), mChildInfosAfter.get(), |
478 | 0 | mChildInfosBeforeCount, mChildInfosAfterCount, bounded); |
479 | 0 |
|
480 | 0 | State currentState = GetState(); |
481 | 0 | bool supportsBefore = SupportsCollapseDirection(Before); |
482 | 0 | bool supportsAfter = SupportsCollapseDirection(After); |
483 | 0 |
|
484 | 0 | const bool isRTL = mOuter->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; |
485 | 0 | bool pastEnd = oldPos > 0 && oldPos > pos; |
486 | 0 | bool pastBegin = oldPos < 0 && oldPos < pos; |
487 | 0 | if (isRTL) { |
488 | 0 | // Swap the boundary checks in RTL mode |
489 | 0 | bool tmp = pastEnd; |
490 | 0 | pastEnd = pastBegin; |
491 | 0 | pastBegin = tmp; |
492 | 0 | } |
493 | 0 | const bool isCollapsedBefore = pastBegin && supportsBefore; |
494 | 0 | const bool isCollapsedAfter = pastEnd && supportsAfter; |
495 | 0 |
|
496 | 0 | // if we are in a collapsed position |
497 | 0 | if (isCollapsedBefore || isCollapsedAfter) |
498 | 0 | { |
499 | 0 | // and we are not collapsed then collapse |
500 | 0 | if (currentState == Dragging) { |
501 | 0 | if (pastEnd) |
502 | 0 | { |
503 | 0 | //printf("Collapse right\n"); |
504 | 0 | if (supportsAfter) |
505 | 0 | { |
506 | 0 | RefPtr<Element> outer = mOuter->mContent->AsElement(); |
507 | 0 | outer->SetAttr(kNameSpaceID_None, nsGkAtoms::substate, |
508 | 0 | NS_LITERAL_STRING("after"), |
509 | 0 | true); |
510 | 0 | outer->SetAttr(kNameSpaceID_None, nsGkAtoms::state, |
511 | 0 | NS_LITERAL_STRING("collapsed"), |
512 | 0 | true); |
513 | 0 | } |
514 | 0 |
|
515 | 0 | } else if (pastBegin) |
516 | 0 | { |
517 | 0 | //printf("Collapse left\n"); |
518 | 0 | if (supportsBefore) |
519 | 0 | { |
520 | 0 | RefPtr<Element> outer = mOuter->mContent->AsElement(); |
521 | 0 | outer->SetAttr(kNameSpaceID_None, nsGkAtoms::substate, |
522 | 0 | NS_LITERAL_STRING("before"), |
523 | 0 | true); |
524 | 0 | outer->SetAttr(kNameSpaceID_None, nsGkAtoms::state, |
525 | 0 | NS_LITERAL_STRING("collapsed"), |
526 | 0 | true); |
527 | 0 | } |
528 | 0 | } |
529 | 0 | } |
530 | 0 | } else { |
531 | 0 | // if we are not in a collapsed position and we are not dragging make sure |
532 | 0 | // we are dragging. |
533 | 0 | if (currentState != Dragging) { |
534 | 0 | mOuter->mContent->AsElement()->SetAttr(kNameSpaceID_None, |
535 | 0 | nsGkAtoms::state, |
536 | 0 | NS_LITERAL_STRING("dragging"), |
537 | 0 | true); |
538 | 0 | } |
539 | 0 | AdjustChildren(aPresContext); |
540 | 0 | } |
541 | 0 |
|
542 | 0 | mDidDrag = true; |
543 | 0 | } |
544 | 0 | } |
545 | | |
546 | | void |
547 | | nsSplitterFrameInner::AddListener() |
548 | 0 | { |
549 | 0 | mOuter->GetContent()-> |
550 | 0 | AddEventListener(NS_LITERAL_STRING("mouseup"), this, false, false); |
551 | 0 | mOuter->GetContent()-> |
552 | 0 | AddEventListener(NS_LITERAL_STRING("mousedown"), this, false, false); |
553 | 0 | mOuter->GetContent()-> |
554 | 0 | AddEventListener(NS_LITERAL_STRING("mousemove"), this, false, false); |
555 | 0 | mOuter->GetContent()-> |
556 | 0 | AddEventListener(NS_LITERAL_STRING("mouseout"), this, false, false); |
557 | 0 | } |
558 | | |
559 | | void |
560 | | nsSplitterFrameInner::RemoveListener() |
561 | 0 | { |
562 | 0 | NS_ENSURE_TRUE_VOID(mOuter); |
563 | 0 | mOuter->GetContent()-> |
564 | 0 | RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, false); |
565 | 0 | mOuter->GetContent()-> |
566 | 0 | RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, false); |
567 | 0 | mOuter->GetContent()-> |
568 | 0 | RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, false); |
569 | 0 | mOuter->GetContent()-> |
570 | 0 | RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, false); |
571 | 0 | } |
572 | | |
573 | | nsresult |
574 | | nsSplitterFrameInner::HandleEvent(dom::Event* aEvent) |
575 | 0 | { |
576 | 0 | nsAutoString eventType; |
577 | 0 | aEvent->GetType(eventType); |
578 | 0 | if (eventType.EqualsLiteral("mouseup")) |
579 | 0 | return MouseUp(aEvent); |
580 | 0 | if (eventType.EqualsLiteral("mousedown")) |
581 | 0 | return MouseDown(aEvent); |
582 | 0 | if (eventType.EqualsLiteral("mousemove") || |
583 | 0 | eventType.EqualsLiteral("mouseout")) |
584 | 0 | return MouseMove(aEvent); |
585 | 0 | |
586 | 0 | MOZ_ASSERT_UNREACHABLE("Unexpected eventType"); |
587 | 0 | return NS_OK; |
588 | 0 | } |
589 | | |
590 | | nsresult |
591 | | nsSplitterFrameInner::MouseUp(Event* aMouseEvent) |
592 | 0 | { |
593 | 0 | NS_ENSURE_TRUE(mOuter, NS_OK); |
594 | 0 | mPressed = false; |
595 | 0 |
|
596 | 0 | nsIPresShell::SetCapturingContent(nullptr, 0); |
597 | 0 |
|
598 | 0 | return NS_OK; |
599 | 0 | } |
600 | | |
601 | | nsresult |
602 | | nsSplitterFrameInner::MouseDown(Event* aMouseEvent) |
603 | 0 | { |
604 | 0 | NS_ENSURE_TRUE(mOuter, NS_OK); |
605 | 0 | dom::MouseEvent* mouseEvent = aMouseEvent->AsMouseEvent(); |
606 | 0 | if (!mouseEvent) { |
607 | 0 | return NS_OK; |
608 | 0 | } |
609 | 0 | |
610 | 0 | // only if left button |
611 | 0 | if (mouseEvent->Button() != 0) |
612 | 0 | return NS_OK; |
613 | 0 | |
614 | 0 | if (SplitterElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled, |
615 | 0 | nsGkAtoms::_true, eCaseMatters)) |
616 | 0 | return NS_OK; |
617 | 0 | |
618 | 0 | mParentBox = nsBox::GetParentXULBox(mOuter); |
619 | 0 | if (!mParentBox) |
620 | 0 | return NS_OK; |
621 | 0 | |
622 | 0 | // get our index |
623 | 0 | nsPresContext* outerPresContext = mOuter->PresContext(); |
624 | 0 | const nsFrameList& siblingList(mParentBox->PrincipalChildList()); |
625 | 0 | int32_t childIndex = siblingList.IndexOf(mOuter); |
626 | 0 | // if it's 0 (or not found) then stop right here. |
627 | 0 | // It might be not found if we're not in the parent's primary frame list. |
628 | 0 | if (childIndex <= 0) |
629 | 0 | return NS_OK; |
630 | 0 | |
631 | 0 | int32_t childCount = siblingList.GetLength(); |
632 | 0 | // if it's the last index then we need to allow for resizeafter="grow" |
633 | 0 | if (childIndex == childCount - 1 && GetResizeAfter() != Grow) |
634 | 0 | return NS_OK; |
635 | 0 | |
636 | 0 | RefPtr<gfxContext> rc = |
637 | 0 | outerPresContext->PresShell()->CreateReferenceRenderingContext(); |
638 | 0 | nsBoxLayoutState state(outerPresContext, rc); |
639 | 0 | mPressed = true; |
640 | 0 |
|
641 | 0 | mDidDrag = false; |
642 | 0 |
|
643 | 0 | EnsureOrient(); |
644 | 0 | bool isHorizontal = !mOuter->IsXULHorizontal(); |
645 | 0 |
|
646 | 0 | ResizeType resizeBefore = GetResizeBefore(); |
647 | 0 | ResizeType resizeAfter = GetResizeAfter(); |
648 | 0 |
|
649 | 0 | mChildInfosBefore = MakeUnique<nsSplitterInfo[]>(childCount); |
650 | 0 | mChildInfosAfter = MakeUnique<nsSplitterInfo[]>(childCount); |
651 | 0 |
|
652 | 0 | // create info 2 lists. One of the children before us and one after. |
653 | 0 | int32_t count = 0; |
654 | 0 | mChildInfosBeforeCount = 0; |
655 | 0 | mChildInfosAfterCount = 0; |
656 | 0 |
|
657 | 0 | nsIFrame* childBox = nsBox::GetChildXULBox(mParentBox); |
658 | 0 |
|
659 | 0 | while (nullptr != childBox) |
660 | 0 | { |
661 | 0 | nsIContent* content = childBox->GetContent(); |
662 | 0 | nsIDocument* doc = content->OwnerDoc(); |
663 | 0 | int32_t dummy; |
664 | 0 | nsAtom* atom = doc->BindingManager()->ResolveTag(content, &dummy); |
665 | 0 |
|
666 | 0 | // skip over any splitters |
667 | 0 | if (atom != nsGkAtoms::splitter) { |
668 | 0 | nsSize prefSize = childBox->GetXULPrefSize(state); |
669 | 0 | nsSize minSize = childBox->GetXULMinSize(state); |
670 | 0 | nsSize maxSize = nsBox::BoundsCheckMinMax(minSize, childBox->GetXULMaxSize(state)); |
671 | 0 | prefSize = nsBox::BoundsCheck(minSize, prefSize, maxSize); |
672 | 0 |
|
673 | 0 | nsSplitterFrame::AddMargin(childBox, minSize); |
674 | 0 | nsSplitterFrame::AddMargin(childBox, prefSize); |
675 | 0 | nsSplitterFrame::AddMargin(childBox, maxSize); |
676 | 0 |
|
677 | 0 | nscoord flex = childBox->GetXULFlex(); |
678 | 0 |
|
679 | 0 | nsMargin margin(0,0,0,0); |
680 | 0 | childBox->GetXULMargin(margin); |
681 | 0 | nsRect r(childBox->GetRect()); |
682 | 0 | r.Inflate(margin); |
683 | 0 |
|
684 | 0 | // We need to check for hidden attribute too, since treecols with |
685 | 0 | // the hidden="true" attribute are not really hidden, just collapsed |
686 | 0 | if (!content->IsElement() || |
687 | 0 | (!content->AsElement()->AttrValueIs(kNameSpaceID_None, |
688 | 0 | nsGkAtoms::fixed, |
689 | 0 | nsGkAtoms::_true, |
690 | 0 | eCaseMatters) && |
691 | 0 | !content->AsElement()->AttrValueIs(kNameSpaceID_None, |
692 | 0 | nsGkAtoms::hidden, |
693 | 0 | nsGkAtoms::_true, |
694 | 0 | eCaseMatters))) { |
695 | 0 | if (count < childIndex && (resizeBefore != Flex || flex > 0)) { |
696 | 0 | mChildInfosBefore[mChildInfosBeforeCount].childElem = content; |
697 | 0 | mChildInfosBefore[mChildInfosBeforeCount].min = isHorizontal ? minSize.width : minSize.height; |
698 | 0 | mChildInfosBefore[mChildInfosBeforeCount].max = isHorizontal ? maxSize.width : maxSize.height; |
699 | 0 | mChildInfosBefore[mChildInfosBeforeCount].current = isHorizontal ? r.width : r.height; |
700 | 0 | mChildInfosBefore[mChildInfosBeforeCount].flex = flex; |
701 | 0 | mChildInfosBefore[mChildInfosBeforeCount].index = count; |
702 | 0 | mChildInfosBefore[mChildInfosBeforeCount].changed = mChildInfosBefore[mChildInfosBeforeCount].current; |
703 | 0 | mChildInfosBeforeCount++; |
704 | 0 | } else if (count > childIndex && (resizeAfter != Flex || flex > 0)) { |
705 | 0 | mChildInfosAfter[mChildInfosAfterCount].childElem = content; |
706 | 0 | mChildInfosAfter[mChildInfosAfterCount].min = isHorizontal ? minSize.width : minSize.height; |
707 | 0 | mChildInfosAfter[mChildInfosAfterCount].max = isHorizontal ? maxSize.width : maxSize.height; |
708 | 0 | mChildInfosAfter[mChildInfosAfterCount].current = isHorizontal ? r.width : r.height; |
709 | 0 | mChildInfosAfter[mChildInfosAfterCount].flex = flex; |
710 | 0 | mChildInfosAfter[mChildInfosAfterCount].index = count; |
711 | 0 | mChildInfosAfter[mChildInfosAfterCount].changed = mChildInfosAfter[mChildInfosAfterCount].current; |
712 | 0 | mChildInfosAfterCount++; |
713 | 0 | } |
714 | 0 | } |
715 | 0 | } |
716 | 0 |
|
717 | 0 | childBox = nsBox::GetNextXULBox(childBox); |
718 | 0 | count++; |
719 | 0 | } |
720 | 0 |
|
721 | 0 | if (!mParentBox->IsXULNormalDirection()) { |
722 | 0 | // The before array is really the after array, and the order needs to be reversed. |
723 | 0 | // First reverse both arrays. |
724 | 0 | Reverse(mChildInfosBefore, mChildInfosBeforeCount); |
725 | 0 | Reverse(mChildInfosAfter, mChildInfosAfterCount); |
726 | 0 |
|
727 | 0 | // Now swap the two arrays. |
728 | 0 | Swap(mChildInfosBeforeCount, mChildInfosAfterCount); |
729 | 0 | Swap(mChildInfosBefore, mChildInfosAfter); |
730 | 0 | } |
731 | 0 |
|
732 | 0 | // if resizebefore is not Farthest, reverse the list because the first child |
733 | 0 | // in the list is the farthest, and we want the first child to be the closest. |
734 | 0 | if (resizeBefore != Farthest) |
735 | 0 | Reverse(mChildInfosBefore, mChildInfosBeforeCount); |
736 | 0 |
|
737 | 0 | // if the resizeafter is the Farthest we must reverse the list because the first child in the list |
738 | 0 | // is the closest we want the first child to be the Farthest. |
739 | 0 | if (resizeAfter == Farthest) |
740 | 0 | Reverse(mChildInfosAfter, mChildInfosAfterCount); |
741 | 0 |
|
742 | 0 | // grow only applys to the children after. If grow is set then no space should be taken out of any children after |
743 | 0 | // us. To do this we just set the size of that list to be 0. |
744 | 0 | if (resizeAfter == Grow) |
745 | 0 | mChildInfosAfterCount = 0; |
746 | 0 |
|
747 | 0 | int32_t c; |
748 | 0 | nsPoint pt = nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(mouseEvent, |
749 | 0 | mParentBox); |
750 | 0 | if (isHorizontal) { |
751 | 0 | c = pt.x; |
752 | 0 | mSplitterPos = mOuter->mRect.x; |
753 | 0 | } else { |
754 | 0 | c = pt.y; |
755 | 0 | mSplitterPos = mOuter->mRect.y; |
756 | 0 | } |
757 | 0 |
|
758 | 0 | mDragStart = c; |
759 | 0 |
|
760 | 0 | //printf("Pressed mDragStart=%d\n",mDragStart); |
761 | 0 |
|
762 | 0 | nsIPresShell::SetCapturingContent(mOuter->GetContent(), CAPTURE_IGNOREALLOWED); |
763 | 0 |
|
764 | 0 | return NS_OK; |
765 | 0 | } |
766 | | |
767 | | nsresult |
768 | | nsSplitterFrameInner::MouseMove(Event* aMouseEvent) |
769 | 0 | { |
770 | 0 | NS_ENSURE_TRUE(mOuter, NS_OK); |
771 | 0 | if (!mPressed) |
772 | 0 | return NS_OK; |
773 | 0 | |
774 | 0 | if (mDragging) |
775 | 0 | return NS_OK; |
776 | 0 | |
777 | 0 | nsCOMPtr<nsIDOMEventListener> kungfuDeathGrip(this); |
778 | 0 | mOuter->mContent->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::state, |
779 | 0 | NS_LITERAL_STRING("dragging"), true); |
780 | 0 |
|
781 | 0 | RemoveListener(); |
782 | 0 | mDragging = true; |
783 | 0 |
|
784 | 0 | return NS_OK; |
785 | 0 | } |
786 | | |
787 | | void |
788 | | nsSplitterFrameInner::Reverse(UniquePtr<nsSplitterInfo[]>& aChildInfos, int32_t aCount) |
789 | 0 | { |
790 | 0 | UniquePtr<nsSplitterInfo[]> infos(new nsSplitterInfo[aCount]); |
791 | 0 |
|
792 | 0 | for (int i=0; i < aCount; i++) |
793 | 0 | infos[i] = aChildInfos[aCount - 1 - i]; |
794 | 0 |
|
795 | 0 | aChildInfos = std::move(infos); |
796 | 0 | } |
797 | | |
798 | | bool |
799 | | nsSplitterFrameInner::SupportsCollapseDirection |
800 | | ( |
801 | | nsSplitterFrameInner::CollapseDirection aDirection |
802 | | ) |
803 | 0 | { |
804 | 0 | static Element::AttrValuesArray strings[] = |
805 | 0 | {&nsGkAtoms::before, &nsGkAtoms::after, &nsGkAtoms::both, nullptr}; |
806 | 0 |
|
807 | 0 | switch (SplitterElement()->FindAttrValueIn(kNameSpaceID_None, |
808 | 0 | nsGkAtoms::collapse, |
809 | 0 | strings, eCaseMatters)) { |
810 | 0 | case 0: |
811 | 0 | return (aDirection == Before); |
812 | 0 | case 1: |
813 | 0 | return (aDirection == After); |
814 | 0 | case 2: |
815 | 0 | return true; |
816 | 0 | } |
817 | 0 | |
818 | 0 | return false; |
819 | 0 | } |
820 | | |
821 | | void |
822 | | nsSplitterFrameInner::UpdateState() |
823 | 0 | { |
824 | 0 | // State Transitions: |
825 | 0 | // Open -> Dragging |
826 | 0 | // Open -> CollapsedBefore |
827 | 0 | // Open -> CollapsedAfter |
828 | 0 | // CollapsedBefore -> Open |
829 | 0 | // CollapsedBefore -> Dragging |
830 | 0 | // CollapsedAfter -> Open |
831 | 0 | // CollapsedAfter -> Dragging |
832 | 0 | // Dragging -> Open |
833 | 0 | // Dragging -> CollapsedBefore (auto collapse) |
834 | 0 | // Dragging -> CollapsedAfter (auto collapse) |
835 | 0 |
|
836 | 0 | State newState = GetState(); |
837 | 0 |
|
838 | 0 | if (newState == mState) { |
839 | 0 | // No change. |
840 | 0 | return; |
841 | 0 | } |
842 | 0 | |
843 | 0 | if ((SupportsCollapseDirection(Before) || SupportsCollapseDirection(After)) && |
844 | 0 | mOuter->GetParent()->IsXULBoxFrame()) { |
845 | 0 | // Find the splitter's immediate sibling. |
846 | 0 | nsIFrame* splitterSibling; |
847 | 0 | if (newState == CollapsedBefore || mState == CollapsedBefore) { |
848 | 0 | splitterSibling = mOuter->GetPrevSibling(); |
849 | 0 | } else { |
850 | 0 | splitterSibling = mOuter->GetNextSibling(); |
851 | 0 | } |
852 | 0 |
|
853 | 0 | if (splitterSibling) { |
854 | 0 | nsCOMPtr<nsIContent> sibling = splitterSibling->GetContent(); |
855 | 0 | if (sibling && sibling->IsElement()) { |
856 | 0 | if (mState == CollapsedBefore || mState == CollapsedAfter) { |
857 | 0 | // CollapsedBefore -> Open |
858 | 0 | // CollapsedBefore -> Dragging |
859 | 0 | // CollapsedAfter -> Open |
860 | 0 | // CollapsedAfter -> Dragging |
861 | 0 | nsContentUtils::AddScriptRunner( |
862 | 0 | new nsUnsetAttrRunnable(sibling->AsElement(), nsGkAtoms::collapsed)); |
863 | 0 | } else if ((mState == Open || mState == Dragging) |
864 | 0 | && (newState == CollapsedBefore || |
865 | 0 | newState == CollapsedAfter)) { |
866 | 0 | // Open -> CollapsedBefore / CollapsedAfter |
867 | 0 | // Dragging -> CollapsedBefore / CollapsedAfter |
868 | 0 | nsContentUtils::AddScriptRunner( |
869 | 0 | new nsSetAttrRunnable(sibling->AsElement(), nsGkAtoms::collapsed, |
870 | 0 | NS_LITERAL_STRING("true"))); |
871 | 0 | } |
872 | 0 | } |
873 | 0 | } |
874 | 0 | } |
875 | 0 | mState = newState; |
876 | 0 | } |
877 | | |
878 | | void |
879 | | nsSplitterFrameInner::EnsureOrient() |
880 | 0 | { |
881 | 0 | bool isHorizontal = !(mParentBox->GetStateBits() & NS_STATE_IS_HORIZONTAL); |
882 | 0 | if (isHorizontal) |
883 | 0 | mOuter->AddStateBits(NS_STATE_IS_HORIZONTAL); |
884 | 0 | else |
885 | 0 | mOuter->RemoveStateBits(NS_STATE_IS_HORIZONTAL); |
886 | 0 | } |
887 | | |
888 | | void |
889 | | nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext) |
890 | 0 | { |
891 | 0 | EnsureOrient(); |
892 | 0 | bool isHorizontal = !mOuter->IsXULHorizontal(); |
893 | 0 |
|
894 | 0 | AdjustChildren(aPresContext, mChildInfosBefore.get(), |
895 | 0 | mChildInfosBeforeCount, isHorizontal); |
896 | 0 | AdjustChildren(aPresContext, mChildInfosAfter.get(), |
897 | 0 | mChildInfosAfterCount, isHorizontal); |
898 | 0 | } |
899 | | |
900 | | static nsIFrame* GetChildBoxForContent(nsIFrame* aParentBox, nsIContent* aContent) |
901 | 0 | { |
902 | 0 | nsIFrame* childBox = nsBox::GetChildXULBox(aParentBox); |
903 | 0 |
|
904 | 0 | while (nullptr != childBox) { |
905 | 0 | if (childBox->GetContent() == aContent) { |
906 | 0 | return childBox; |
907 | 0 | } |
908 | 0 | childBox = nsBox::GetNextXULBox(childBox); |
909 | 0 | } |
910 | 0 | return nullptr; |
911 | 0 | } |
912 | | |
913 | | void |
914 | | nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal) |
915 | 0 | { |
916 | 0 | ///printf("------- AdjustChildren------\n"); |
917 | 0 |
|
918 | 0 | nsBoxLayoutState state(aPresContext); |
919 | 0 |
|
920 | 0 | nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); |
921 | 0 |
|
922 | 0 | // first set all the widths. |
923 | 0 | nsIFrame* child = nsBox::GetChildXULBox(mOuter); |
924 | 0 | while(child) |
925 | 0 | { |
926 | 0 | SetPreferredSize(state, child, onePixel, aIsHorizontal, nullptr); |
927 | 0 | child = nsBox::GetNextXULBox(child); |
928 | 0 | } |
929 | 0 |
|
930 | 0 | // now set our changed widths. |
931 | 0 | for (int i=0; i < aCount; i++) |
932 | 0 | { |
933 | 0 | nscoord pref = aChildInfos[i].changed; |
934 | 0 | nsIFrame* childBox = GetChildBoxForContent(mParentBox, aChildInfos[i].childElem); |
935 | 0 |
|
936 | 0 | if (childBox) { |
937 | 0 | SetPreferredSize(state, childBox, onePixel, aIsHorizontal, &pref); |
938 | 0 | } |
939 | 0 | } |
940 | 0 | } |
941 | | |
942 | | void |
943 | | nsSplitterFrameInner::SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize) |
944 | 0 | { |
945 | 0 | nsRect rect(aChildBox->GetRect()); |
946 | 0 | nscoord pref = 0; |
947 | 0 |
|
948 | 0 | if (!aSize) |
949 | 0 | { |
950 | 0 | if (aIsHorizontal) |
951 | 0 | pref = rect.width; |
952 | 0 | else |
953 | 0 | pref = rect.height; |
954 | 0 | } else { |
955 | 0 | pref = *aSize; |
956 | 0 | } |
957 | 0 |
|
958 | 0 | nsMargin margin(0,0,0,0); |
959 | 0 | aChildBox->GetXULMargin(margin); |
960 | 0 |
|
961 | 0 | RefPtr<nsAtom> attribute; |
962 | 0 |
|
963 | 0 | if (aIsHorizontal) { |
964 | 0 | pref -= (margin.left + margin.right); |
965 | 0 | attribute = nsGkAtoms::width; |
966 | 0 | } else { |
967 | 0 | pref -= (margin.top + margin.bottom); |
968 | 0 | attribute = nsGkAtoms::height; |
969 | 0 | } |
970 | 0 |
|
971 | 0 | nsIContent* content = aChildBox->GetContent(); |
972 | 0 | if (!content->IsElement()) { |
973 | 0 | return; |
974 | 0 | } |
975 | 0 | |
976 | 0 | // set its preferred size. |
977 | 0 | nsAutoString prefValue; |
978 | 0 | prefValue.AppendInt(pref/aOnePixel); |
979 | 0 | if (content->AsElement()->AttrValueIs(kNameSpaceID_None, attribute, |
980 | 0 | prefValue, eCaseMatters)) { |
981 | 0 | return; |
982 | 0 | } |
983 | 0 | |
984 | 0 | AutoWeakFrame weakBox(aChildBox); |
985 | 0 | content->AsElement()->SetAttr(kNameSpaceID_None, attribute, prefValue, true); |
986 | 0 | NS_ENSURE_TRUE_VOID(weakBox.IsAlive()); |
987 | 0 | aState.PresShell()->FrameNeedsReflow(aChildBox, nsIPresShell::eStyleChange, |
988 | 0 | NS_FRAME_IS_DIRTY); |
989 | 0 | } |
990 | | |
991 | | |
992 | | void |
993 | | nsSplitterFrameInner::AddRemoveSpace(nscoord aDiff, |
994 | | nsSplitterInfo* aChildInfos, |
995 | | int32_t aCount, |
996 | | int32_t& aSpaceLeft) |
997 | 0 | { |
998 | 0 | aSpaceLeft = 0; |
999 | 0 |
|
1000 | 0 | for (int i=0; i < aCount; i++) { |
1001 | 0 | nscoord min = aChildInfos[i].min; |
1002 | 0 | nscoord max = aChildInfos[i].max; |
1003 | 0 | nscoord& c = aChildInfos[i].changed; |
1004 | 0 |
|
1005 | 0 | // figure our how much space to add or remove |
1006 | 0 | if (c + aDiff < min) { |
1007 | 0 | aDiff += (c - min); |
1008 | 0 | c = min; |
1009 | 0 | } else if (c + aDiff > max) { |
1010 | 0 | aDiff -= (max - c); |
1011 | 0 | c = max; |
1012 | 0 | } else { |
1013 | 0 | c += aDiff; |
1014 | 0 | aDiff = 0; |
1015 | 0 | } |
1016 | 0 |
|
1017 | 0 | // there is not space left? We are done |
1018 | 0 | if (aDiff == 0) |
1019 | 0 | break; |
1020 | 0 | } |
1021 | 0 |
|
1022 | 0 | aSpaceLeft = aDiff; |
1023 | 0 | } |
1024 | | |
1025 | | /** |
1026 | | * Ok if we want to resize a child we will know the actual size in pixels we want it to be. |
1027 | | * This is not the preferred size. But they only way we can change a child is my manipulating its |
1028 | | * preferred size. So give the actual pixel size this return method will return figure out the preferred |
1029 | | * size and set it. |
1030 | | */ |
1031 | | |
1032 | | void |
1033 | | nsSplitterFrameInner::ResizeChildTo(nscoord& aDiff, |
1034 | | nsSplitterInfo* aChildrenBeforeInfos, |
1035 | | nsSplitterInfo* aChildrenAfterInfos, |
1036 | | int32_t aChildrenBeforeCount, |
1037 | | int32_t aChildrenAfterCount, |
1038 | | bool aBounded) |
1039 | 0 | { |
1040 | 0 | nscoord spaceLeft; |
1041 | 0 | AddRemoveSpace(aDiff, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft); |
1042 | 0 |
|
1043 | 0 | // if there is any space left over remove it from the dif we were originally given |
1044 | 0 | aDiff -= spaceLeft; |
1045 | 0 | AddRemoveSpace(-aDiff, aChildrenAfterInfos,aChildrenAfterCount,spaceLeft); |
1046 | 0 |
|
1047 | 0 | if (spaceLeft != 0) { |
1048 | 0 | if (aBounded) { |
1049 | 0 | aDiff += spaceLeft; |
1050 | 0 | AddRemoveSpace(spaceLeft, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft); |
1051 | 0 | } |
1052 | 0 | } |
1053 | 0 | } |