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/. */
9 // Netscape Communications
11 // See documentation in associated header file
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"
41 using namespace mozilla
;
43 using mozilla::dom::Event
;
45 class nsSplitterInfo
{
51 nsCOMPtr
<nsIContent
> childElem
;
56 class nsSplitterFrameInner final
: public nsIDOMEventListener
{
58 virtual ~nsSplitterFrameInner();
62 NS_DECL_NSIDOMEVENTLISTENER
64 explicit nsSplitterFrameInner(nsSplitterFrame
* aSplitter
)
68 mChildInfosBeforeCount(0),
69 mChildInfosAfterCount(0),
77 void Disconnect() { mOuter
= nullptr; }
79 nsresult
MouseDown(Event
* aMouseEvent
);
80 nsresult
MouseUp(Event
* aMouseEvent
);
81 nsresult
MouseMove(Event
* aMouseEvent
);
83 void MouseDrag(nsPresContext
* aPresContext
, WidgetGUIEvent
* aEvent
);
84 void MouseUp(nsPresContext
* aPresContext
, WidgetGUIEvent
* aEvent
);
86 void AdjustChildren(nsPresContext
* aPresContext
);
87 void AdjustChildren(nsPresContext
* aPresContext
, nsSplitterInfo
* aChildInfos
,
88 int32_t aCount
, bool aIsHorizontal
);
90 void AddRemoveSpace(nscoord aDiff
, nsSplitterInfo
* aChildInfos
,
91 int32_t aCount
, int32_t& aSpaceLeft
);
93 void ResizeChildTo(nscoord
& aDiff
, nsSplitterInfo
* aChildrenBeforeInfos
,
94 nsSplitterInfo
* aChildrenAfterInfos
,
95 int32_t aChildrenBeforeCount
, int32_t aChildrenAfterCount
,
101 void RemoveListener();
103 enum ResizeType
{ Closest
, Farthest
, Flex
, Grow
};
104 enum State
{ Open
, CollapsedBefore
, CollapsedAfter
, Dragging
};
105 enum CollapseDirection
{ Before
, After
};
107 ResizeType
GetResizeBefore();
108 ResizeType
GetResizeAfter();
111 void Reverse(UniquePtr
<nsSplitterInfo
[]>& aIndexes
, int32_t aCount
);
112 bool SupportsCollapseDirection(CollapseDirection aDirection
);
115 void SetPreferredSize(nsBoxLayoutState
& aState
, nsIFrame
* aChildBox
,
116 nscoord aOnePixel
, bool aIsHorizontal
, nscoord
* aSize
);
118 nsSplitterFrame
* mOuter
;
121 nsIFrame
* mParentBox
;
123 UniquePtr
<nsSplitterInfo
[]> mChildInfosBefore
;
124 UniquePtr
<nsSplitterInfo
[]> mChildInfosAfter
;
125 int32_t mChildInfosBeforeCount
;
126 int32_t mChildInfosAfterCount
;
128 nscoord mSplitterPos
;
131 const Element
* SplitterElement() const {
132 return mOuter
->GetContent()->AsElement();
136 NS_IMPL_ISUPPORTS(nsSplitterFrameInner
, nsIDOMEventListener
)
138 nsSplitterFrameInner::ResizeType
nsSplitterFrameInner::GetResizeBefore() {
139 static Element::AttrValuesArray strings
[] = {nsGkAtoms::farthest
,
140 nsGkAtoms::flex
, nullptr};
141 switch (SplitterElement()->FindAttrValueIn(
142 kNameSpaceID_None
, nsGkAtoms::resizebefore
, strings
, eCaseMatters
)) {
151 nsSplitterFrameInner::~nsSplitterFrameInner() {}
153 nsSplitterFrameInner::ResizeType
nsSplitterFrameInner::GetResizeAfter() {
154 static Element::AttrValuesArray strings
[] = {
155 nsGkAtoms::farthest
, nsGkAtoms::flex
, nsGkAtoms::grow
, nullptr};
156 switch (SplitterElement()->FindAttrValueIn(
157 kNameSpaceID_None
, nsGkAtoms::resizeafter
, strings
, eCaseMatters
)) {
168 nsSplitterFrameInner::State
nsSplitterFrameInner::GetState() {
169 static Element::AttrValuesArray strings
[] = {nsGkAtoms::dragging
,
170 nsGkAtoms::collapsed
, nullptr};
171 static Element::AttrValuesArray strings_substate
[] = {
172 nsGkAtoms::before
, nsGkAtoms::after
, nullptr};
173 switch (SplitterElement()->FindAttrValueIn(
174 kNameSpaceID_None
, nsGkAtoms::state
, strings
, eCaseMatters
)) {
178 switch (SplitterElement()->FindAttrValueIn(
179 kNameSpaceID_None
, nsGkAtoms::substate
, strings_substate
,
182 return CollapsedBefore
;
184 return CollapsedAfter
;
186 if (SupportsCollapseDirection(After
)) return CollapsedAfter
;
187 return CollapsedBefore
;
194 // NS_NewSplitterFrame
196 // Creates a new Toolbar frame and returns it
198 nsIFrame
* NS_NewSplitterFrame(nsIPresShell
* aPresShell
, ComputedStyle
* aStyle
) {
199 return new (aPresShell
) nsSplitterFrame(aStyle
);
202 NS_IMPL_FRAMEARENA_HELPERS(nsSplitterFrame
)
204 nsSplitterFrame::nsSplitterFrame(ComputedStyle
* aStyle
)
205 : nsBoxFrame(aStyle
, kClassID
), mInner(0) {}
207 void nsSplitterFrame::DestroyFrom(nsIFrame
* aDestructRoot
,
208 PostDestroyData
& aPostDestroyData
) {
210 mInner
->RemoveListener();
211 mInner
->Disconnect();
215 nsBoxFrame::DestroyFrom(aDestructRoot
, aPostDestroyData
);
218 nsresult
nsSplitterFrame::GetCursor(const nsPoint
& aPoint
,
219 nsIFrame::Cursor
& aCursor
) {
220 return nsBoxFrame::GetCursor(aPoint
, aCursor
);
223 if (IsXULHorizontal())
224 aCursor = NS_STYLE_CURSOR_N_RESIZE;
226 aCursor = NS_STYLE_CURSOR_W_RESIZE;
232 nsresult
nsSplitterFrame::AttributeChanged(int32_t aNameSpaceID
,
236 nsBoxFrame::AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
237 if (aAttribute
== nsGkAtoms::state
) {
238 mInner
->UpdateState();
245 * Initialize us. If we are in a box get our alignment so we know what direction
248 void nsSplitterFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
249 nsIFrame
* aPrevInFlow
) {
251 mInner
= new nsSplitterFrameInner(this);
255 // determine orientation of parent, and if vertical, set orient to vertical
256 // on splitter content, then re-resolve style
257 // XXXbz this is pretty messed up, since this can change whether we should
258 // have a frame at all. This really needs a better solution.
259 if (aParent
&& aParent
->IsXULBoxFrame()) {
260 if (!aParent
->IsXULHorizontal()) {
261 if (!nsContentUtils::HasNonEmptyAttr(aContent
, kNameSpaceID_None
,
262 nsGkAtoms::orient
)) {
263 aContent
->AsElement()->SetAttr(kNameSpaceID_None
, nsGkAtoms::orient
,
264 NS_LITERAL_STRING("vertical"), false);
269 nsBoxFrame::Init(aContent
, aParent
, aPrevInFlow
);
271 mInner
->mState
= nsSplitterFrameInner::Open
;
272 mInner
->AddListener();
273 mInner
->mParentBox
= nullptr;
277 nsSplitterFrame::DoXULLayout(nsBoxLayoutState
& aState
) {
278 if (GetStateBits() & NS_FRAME_FIRST_REFLOW
) {
279 mInner
->mParentBox
= nsBox::GetParentXULBox(this);
280 mInner
->UpdateState();
283 return nsBoxFrame::DoXULLayout(aState
);
286 void nsSplitterFrame::GetInitialOrientation(bool& aIsHorizontal
) {
287 nsIFrame
* box
= nsBox::GetParentXULBox(this);
289 aIsHorizontal
= !box
->IsXULHorizontal();
291 nsBoxFrame::GetInitialOrientation(aIsHorizontal
);
295 nsSplitterFrame::HandlePress(nsPresContext
* aPresContext
,
296 WidgetGUIEvent
* aEvent
,
297 nsEventStatus
* aEventStatus
) {
302 nsSplitterFrame::HandleMultiplePress(nsPresContext
* aPresContext
,
303 WidgetGUIEvent
* aEvent
,
304 nsEventStatus
* aEventStatus
,
310 nsSplitterFrame::HandleDrag(nsPresContext
* aPresContext
, WidgetGUIEvent
* aEvent
,
311 nsEventStatus
* aEventStatus
) {
316 nsSplitterFrame::HandleRelease(nsPresContext
* aPresContext
,
317 WidgetGUIEvent
* aEvent
,
318 nsEventStatus
* aEventStatus
) {
322 void nsSplitterFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
323 const nsDisplayListSet
& aLists
) {
324 nsBoxFrame::BuildDisplayList(aBuilder
, aLists
);
326 // if the mouse is captured always return us as the frame.
327 if (mInner
->mDragging
&& aBuilder
->IsForEventDelivery()) {
328 // XXX It's probably better not to check visibility here, right?
329 aLists
.Outlines()->AppendToTop(
330 MakeDisplayItem
<nsDisplayEventReceiver
>(aBuilder
, this));
335 nsresult
nsSplitterFrame::HandleEvent(nsPresContext
* aPresContext
,
336 WidgetGUIEvent
* aEvent
,
337 nsEventStatus
* aEventStatus
) {
338 NS_ENSURE_ARG_POINTER(aEventStatus
);
339 if (nsEventStatus_eConsumeNoDefault
== *aEventStatus
) {
343 AutoWeakFrame
weakFrame(this);
344 RefPtr
<nsSplitterFrameInner
> inner(mInner
);
345 switch (aEvent
->mMessage
) {
347 inner
->MouseDrag(aPresContext
, aEvent
);
351 if (aEvent
->AsMouseEvent()->button
== WidgetMouseEvent::eLeftButton
) {
352 inner
->MouseUp(aPresContext
, aEvent
);
360 NS_ENSURE_STATE(weakFrame
.IsAlive());
361 return nsBoxFrame::HandleEvent(aPresContext
, aEvent
, aEventStatus
);
364 void nsSplitterFrameInner::MouseUp(nsPresContext
* aPresContext
,
365 WidgetGUIEvent
* aEvent
) {
366 if (mDragging
&& mOuter
) {
367 AdjustChildren(aPresContext
);
369 nsIPresShell::SetCapturingContent(nullptr,
370 0); // XXXndeakin is this needed?
372 State newState
= GetState();
373 // if the state is dragging then make it Open.
374 if (newState
== Dragging
) {
375 mOuter
->mContent
->AsElement()->SetAttr(
376 kNameSpaceID_None
, nsGkAtoms::state
, EmptyString(), true);
381 // if we dragged then fire a command event.
383 RefPtr
<nsXULElement
> element
=
384 nsXULElement::FromNode(mOuter
->GetContent());
385 element
->DoCommand();
388 // printf("MouseUp\n");
391 mChildInfosBefore
= nullptr;
392 mChildInfosAfter
= nullptr;
393 mChildInfosBeforeCount
= 0;
394 mChildInfosAfterCount
= 0;
397 void nsSplitterFrameInner::MouseDrag(nsPresContext
* aPresContext
,
398 WidgetGUIEvent
* aEvent
) {
399 if (mDragging
&& mOuter
) {
400 // printf("Dragging\n");
402 bool isHorizontal
= !mOuter
->IsXULHorizontal();
403 // convert coord to pixels
405 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
, mParentBox
);
406 nscoord pos
= isHorizontal
? pt
.x
: pt
.y
;
408 // mDragStart is in frame coordinates
409 nscoord start
= mDragStart
;
411 // take our current position and subtract the start location
414 // printf("Diff=%d\n", pos);
416 ResizeType resizeAfter
= GetResizeAfter();
420 if (resizeAfter
== nsSplitterFrameInner::Grow
)
426 for (i
= 0; i
< mChildInfosBeforeCount
; i
++)
427 mChildInfosBefore
[i
].changed
= mChildInfosBefore
[i
].current
;
429 for (i
= 0; i
< mChildInfosAfterCount
; i
++)
430 mChildInfosAfter
[i
].changed
= mChildInfosAfter
[i
].current
;
432 nscoord oldPos
= pos
;
434 ResizeChildTo(pos
, mChildInfosBefore
.get(), mChildInfosAfter
.get(),
435 mChildInfosBeforeCount
, mChildInfosAfterCount
, bounded
);
437 State currentState
= GetState();
438 bool supportsBefore
= SupportsCollapseDirection(Before
);
439 bool supportsAfter
= SupportsCollapseDirection(After
);
442 mOuter
->StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
;
443 bool pastEnd
= oldPos
> 0 && oldPos
> pos
;
444 bool pastBegin
= oldPos
< 0 && oldPos
< pos
;
446 // Swap the boundary checks in RTL mode
451 const bool isCollapsedBefore
= pastBegin
&& supportsBefore
;
452 const bool isCollapsedAfter
= pastEnd
&& supportsAfter
;
454 // if we are in a collapsed position
455 if (isCollapsedBefore
|| isCollapsedAfter
) {
456 // and we are not collapsed then collapse
457 if (currentState
== Dragging
) {
459 // printf("Collapse right\n");
461 RefPtr
<Element
> outer
= mOuter
->mContent
->AsElement();
462 outer
->SetAttr(kNameSpaceID_None
, nsGkAtoms::substate
,
463 NS_LITERAL_STRING("after"), true);
464 outer
->SetAttr(kNameSpaceID_None
, nsGkAtoms::state
,
465 NS_LITERAL_STRING("collapsed"), true);
468 } else if (pastBegin
) {
469 // printf("Collapse left\n");
470 if (supportsBefore
) {
471 RefPtr
<Element
> outer
= mOuter
->mContent
->AsElement();
472 outer
->SetAttr(kNameSpaceID_None
, nsGkAtoms::substate
,
473 NS_LITERAL_STRING("before"), true);
474 outer
->SetAttr(kNameSpaceID_None
, nsGkAtoms::state
,
475 NS_LITERAL_STRING("collapsed"), true);
480 // if we are not in a collapsed position and we are not dragging make sure
482 if (currentState
!= Dragging
) {
483 mOuter
->mContent
->AsElement()->SetAttr(
484 kNameSpaceID_None
, nsGkAtoms::state
, NS_LITERAL_STRING("dragging"),
487 AdjustChildren(aPresContext
);
494 void nsSplitterFrameInner::AddListener() {
495 mOuter
->GetContent()->AddEventListener(NS_LITERAL_STRING("mouseup"), this,
497 mOuter
->GetContent()->AddEventListener(NS_LITERAL_STRING("mousedown"), this,
499 mOuter
->GetContent()->AddEventListener(NS_LITERAL_STRING("mousemove"), this,
501 mOuter
->GetContent()->AddEventListener(NS_LITERAL_STRING("mouseout"), this,
505 void nsSplitterFrameInner::RemoveListener() {
506 NS_ENSURE_TRUE_VOID(mOuter
);
507 mOuter
->GetContent()->RemoveEventListener(NS_LITERAL_STRING("mouseup"), this,
509 mOuter
->GetContent()->RemoveEventListener(NS_LITERAL_STRING("mousedown"),
511 mOuter
->GetContent()->RemoveEventListener(NS_LITERAL_STRING("mousemove"),
513 mOuter
->GetContent()->RemoveEventListener(NS_LITERAL_STRING("mouseout"), this,
517 nsresult
nsSplitterFrameInner::HandleEvent(dom::Event
* aEvent
) {
518 nsAutoString eventType
;
519 aEvent
->GetType(eventType
);
520 if (eventType
.EqualsLiteral("mouseup")) return MouseUp(aEvent
);
521 if (eventType
.EqualsLiteral("mousedown")) return MouseDown(aEvent
);
522 if (eventType
.EqualsLiteral("mousemove") ||
523 eventType
.EqualsLiteral("mouseout"))
524 return MouseMove(aEvent
);
526 MOZ_ASSERT_UNREACHABLE("Unexpected eventType");
530 nsresult
nsSplitterFrameInner::MouseUp(Event
* aMouseEvent
) {
531 NS_ENSURE_TRUE(mOuter
, NS_OK
);
534 nsIPresShell::SetCapturingContent(nullptr, 0);
539 nsresult
nsSplitterFrameInner::MouseDown(Event
* aMouseEvent
) {
540 NS_ENSURE_TRUE(mOuter
, NS_OK
);
541 dom::MouseEvent
* mouseEvent
= aMouseEvent
->AsMouseEvent();
546 // only if left button
547 if (mouseEvent
->Button() != 0) return NS_OK
;
549 if (SplitterElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::disabled
,
550 nsGkAtoms::_true
, eCaseMatters
))
553 mParentBox
= nsBox::GetParentXULBox(mOuter
);
554 if (!mParentBox
) return NS_OK
;
557 nsPresContext
* outerPresContext
= mOuter
->PresContext();
558 const nsFrameList
& siblingList(mParentBox
->PrincipalChildList());
559 int32_t childIndex
= siblingList
.IndexOf(mOuter
);
560 // if it's 0 (or not found) then stop right here.
561 // It might be not found if we're not in the parent's primary frame list.
562 if (childIndex
<= 0) return NS_OK
;
564 int32_t childCount
= siblingList
.GetLength();
565 // if it's the last index then we need to allow for resizeafter="grow"
566 if (childIndex
== childCount
- 1 && GetResizeAfter() != Grow
) return NS_OK
;
568 RefPtr
<gfxContext
> rc
=
569 outerPresContext
->PresShell()->CreateReferenceRenderingContext();
570 nsBoxLayoutState
state(outerPresContext
, rc
);
576 bool isHorizontal
= !mOuter
->IsXULHorizontal();
578 ResizeType resizeBefore
= GetResizeBefore();
579 ResizeType resizeAfter
= GetResizeAfter();
581 mChildInfosBefore
= MakeUnique
<nsSplitterInfo
[]>(childCount
);
582 mChildInfosAfter
= MakeUnique
<nsSplitterInfo
[]>(childCount
);
584 // create info 2 lists. One of the children before us and one after.
586 mChildInfosBeforeCount
= 0;
587 mChildInfosAfterCount
= 0;
589 nsIFrame
* childBox
= nsBox::GetChildXULBox(mParentBox
);
591 while (nullptr != childBox
) {
592 nsIContent
* content
= childBox
->GetContent();
593 nsIDocument
* doc
= content
->OwnerDoc();
595 nsAtom
* atom
= doc
->BindingManager()->ResolveTag(content
, &dummy
);
597 // skip over any splitters
598 if (atom
!= nsGkAtoms::splitter
) {
599 nsSize prefSize
= childBox
->GetXULPrefSize(state
);
600 nsSize minSize
= childBox
->GetXULMinSize(state
);
602 nsBox::BoundsCheckMinMax(minSize
, childBox
->GetXULMaxSize(state
));
603 prefSize
= nsBox::BoundsCheck(minSize
, prefSize
, maxSize
);
605 nsSplitterFrame::AddMargin(childBox
, minSize
);
606 nsSplitterFrame::AddMargin(childBox
, prefSize
);
607 nsSplitterFrame::AddMargin(childBox
, maxSize
);
609 nscoord flex
= childBox
->GetXULFlex();
611 nsMargin
margin(0, 0, 0, 0);
612 childBox
->GetXULMargin(margin
);
613 nsRect
r(childBox
->GetRect());
616 // We need to check for hidden attribute too, since treecols with
617 // the hidden="true" attribute are not really hidden, just collapsed
618 if (!content
->IsElement() || (!content
->AsElement()->AttrValueIs(
619 kNameSpaceID_None
, nsGkAtoms::fixed
,
620 nsGkAtoms::_true
, eCaseMatters
) &&
621 !content
->AsElement()->AttrValueIs(
622 kNameSpaceID_None
, nsGkAtoms::hidden
,
623 nsGkAtoms::_true
, eCaseMatters
))) {
624 if (count
< childIndex
&& (resizeBefore
!= Flex
|| flex
> 0)) {
625 mChildInfosBefore
[mChildInfosBeforeCount
].childElem
= content
;
626 mChildInfosBefore
[mChildInfosBeforeCount
].min
=
627 isHorizontal
? minSize
.width
: minSize
.height
;
628 mChildInfosBefore
[mChildInfosBeforeCount
].max
=
629 isHorizontal
? maxSize
.width
: maxSize
.height
;
630 mChildInfosBefore
[mChildInfosBeforeCount
].current
=
631 isHorizontal
? r
.width
: r
.height
;
632 mChildInfosBefore
[mChildInfosBeforeCount
].flex
= flex
;
633 mChildInfosBefore
[mChildInfosBeforeCount
].index
= count
;
634 mChildInfosBefore
[mChildInfosBeforeCount
].changed
=
635 mChildInfosBefore
[mChildInfosBeforeCount
].current
;
636 mChildInfosBeforeCount
++;
637 } else if (count
> childIndex
&& (resizeAfter
!= Flex
|| flex
> 0)) {
638 mChildInfosAfter
[mChildInfosAfterCount
].childElem
= content
;
639 mChildInfosAfter
[mChildInfosAfterCount
].min
=
640 isHorizontal
? minSize
.width
: minSize
.height
;
641 mChildInfosAfter
[mChildInfosAfterCount
].max
=
642 isHorizontal
? maxSize
.width
: maxSize
.height
;
643 mChildInfosAfter
[mChildInfosAfterCount
].current
=
644 isHorizontal
? r
.width
: r
.height
;
645 mChildInfosAfter
[mChildInfosAfterCount
].flex
= flex
;
646 mChildInfosAfter
[mChildInfosAfterCount
].index
= count
;
647 mChildInfosAfter
[mChildInfosAfterCount
].changed
=
648 mChildInfosAfter
[mChildInfosAfterCount
].current
;
649 mChildInfosAfterCount
++;
654 childBox
= nsBox::GetNextXULBox(childBox
);
658 if (!mParentBox
->IsXULNormalDirection()) {
659 // The before array is really the after array, and the order needs to be
660 // reversed. First reverse both arrays.
661 Reverse(mChildInfosBefore
, mChildInfosBeforeCount
);
662 Reverse(mChildInfosAfter
, mChildInfosAfterCount
);
664 // Now swap the two arrays.
665 Swap(mChildInfosBeforeCount
, mChildInfosAfterCount
);
666 Swap(mChildInfosBefore
, mChildInfosAfter
);
669 // if resizebefore is not Farthest, reverse the list because the first child
670 // in the list is the farthest, and we want the first child to be the closest.
671 if (resizeBefore
!= Farthest
)
672 Reverse(mChildInfosBefore
, mChildInfosBeforeCount
);
674 // if the resizeafter is the Farthest we must reverse the list because the
675 // first child in the list is the closest we want the first child to be the
677 if (resizeAfter
== Farthest
) Reverse(mChildInfosAfter
, mChildInfosAfterCount
);
679 // grow only applys to the children after. If grow is set then no space should
680 // be taken out of any children after us. To do this we just set the size of
681 // that list to be 0.
682 if (resizeAfter
== Grow
) mChildInfosAfterCount
= 0;
686 nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(mouseEvent
, mParentBox
);
689 mSplitterPos
= mOuter
->mRect
.x
;
692 mSplitterPos
= mOuter
->mRect
.y
;
697 // printf("Pressed mDragStart=%d\n",mDragStart);
699 nsIPresShell::SetCapturingContent(mOuter
->GetContent(),
700 CAPTURE_IGNOREALLOWED
);
705 nsresult
nsSplitterFrameInner::MouseMove(Event
* aMouseEvent
) {
706 NS_ENSURE_TRUE(mOuter
, NS_OK
);
707 if (!mPressed
) return NS_OK
;
709 if (mDragging
) return NS_OK
;
711 nsCOMPtr
<nsIDOMEventListener
> kungfuDeathGrip(this);
712 mOuter
->mContent
->AsElement()->SetAttr(kNameSpaceID_None
, nsGkAtoms::state
,
713 NS_LITERAL_STRING("dragging"), true);
721 void nsSplitterFrameInner::Reverse(UniquePtr
<nsSplitterInfo
[]>& aChildInfos
,
723 UniquePtr
<nsSplitterInfo
[]> infos(new nsSplitterInfo
[aCount
]);
725 for (int i
= 0; i
< aCount
; i
++) infos
[i
] = aChildInfos
[aCount
- 1 - i
];
727 aChildInfos
= std::move(infos
);
730 bool nsSplitterFrameInner::SupportsCollapseDirection(
731 nsSplitterFrameInner::CollapseDirection aDirection
) {
732 static Element::AttrValuesArray strings
[] = {
733 nsGkAtoms::before
, nsGkAtoms::after
, nsGkAtoms::both
, nullptr};
735 switch (SplitterElement()->FindAttrValueIn(
736 kNameSpaceID_None
, nsGkAtoms::collapse
, strings
, eCaseMatters
)) {
738 return (aDirection
== Before
);
740 return (aDirection
== After
);
748 void nsSplitterFrameInner::UpdateState() {
749 // State Transitions:
751 // Open -> CollapsedBefore
752 // Open -> CollapsedAfter
753 // CollapsedBefore -> Open
754 // CollapsedBefore -> Dragging
755 // CollapsedAfter -> Open
756 // CollapsedAfter -> Dragging
758 // Dragging -> CollapsedBefore (auto collapse)
759 // Dragging -> CollapsedAfter (auto collapse)
761 State newState
= GetState();
763 if (newState
== mState
) {
768 if ((SupportsCollapseDirection(Before
) || SupportsCollapseDirection(After
)) &&
769 mOuter
->GetParent()->IsXULBoxFrame()) {
770 // Find the splitter's immediate sibling.
771 nsIFrame
* splitterSibling
;
772 if (newState
== CollapsedBefore
|| mState
== CollapsedBefore
) {
773 splitterSibling
= mOuter
->GetPrevSibling();
775 splitterSibling
= mOuter
->GetNextSibling();
778 if (splitterSibling
) {
779 nsCOMPtr
<nsIContent
> sibling
= splitterSibling
->GetContent();
780 if (sibling
&& sibling
->IsElement()) {
781 if (mState
== CollapsedBefore
|| mState
== CollapsedAfter
) {
782 // CollapsedBefore -> Open
783 // CollapsedBefore -> Dragging
784 // CollapsedAfter -> Open
785 // CollapsedAfter -> Dragging
786 nsContentUtils::AddScriptRunner(new nsUnsetAttrRunnable(
787 sibling
->AsElement(), nsGkAtoms::collapsed
));
788 } else if ((mState
== Open
|| mState
== Dragging
) &&
789 (newState
== CollapsedBefore
||
790 newState
== CollapsedAfter
)) {
791 // Open -> CollapsedBefore / CollapsedAfter
792 // Dragging -> CollapsedBefore / CollapsedAfter
793 nsContentUtils::AddScriptRunner(
794 new nsSetAttrRunnable(sibling
->AsElement(), nsGkAtoms::collapsed
,
795 NS_LITERAL_STRING("true")));
803 void nsSplitterFrameInner::EnsureOrient() {
804 bool isHorizontal
= !(mParentBox
->GetStateBits() & NS_STATE_IS_HORIZONTAL
);
806 mOuter
->AddStateBits(NS_STATE_IS_HORIZONTAL
);
808 mOuter
->RemoveStateBits(NS_STATE_IS_HORIZONTAL
);
811 void nsSplitterFrameInner::AdjustChildren(nsPresContext
* aPresContext
) {
813 bool isHorizontal
= !mOuter
->IsXULHorizontal();
815 AdjustChildren(aPresContext
, mChildInfosBefore
.get(), mChildInfosBeforeCount
,
817 AdjustChildren(aPresContext
, mChildInfosAfter
.get(), mChildInfosAfterCount
,
821 static nsIFrame
* GetChildBoxForContent(nsIFrame
* aParentBox
,
822 nsIContent
* aContent
) {
823 nsIFrame
* childBox
= nsBox::GetChildXULBox(aParentBox
);
825 while (nullptr != childBox
) {
826 if (childBox
->GetContent() == aContent
) {
829 childBox
= nsBox::GetNextXULBox(childBox
);
834 void nsSplitterFrameInner::AdjustChildren(nsPresContext
* aPresContext
,
835 nsSplitterInfo
* aChildInfos
,
836 int32_t aCount
, bool aIsHorizontal
) {
837 /// printf("------- AdjustChildren------\n");
839 nsBoxLayoutState
state(aPresContext
);
841 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
843 // first set all the widths.
844 nsIFrame
* child
= nsBox::GetChildXULBox(mOuter
);
846 SetPreferredSize(state
, child
, onePixel
, aIsHorizontal
, nullptr);
847 child
= nsBox::GetNextXULBox(child
);
850 // now set our changed widths.
851 for (int i
= 0; i
< aCount
; i
++) {
852 nscoord pref
= aChildInfos
[i
].changed
;
854 GetChildBoxForContent(mParentBox
, aChildInfos
[i
].childElem
);
857 SetPreferredSize(state
, childBox
, onePixel
, aIsHorizontal
, &pref
);
862 void nsSplitterFrameInner::SetPreferredSize(nsBoxLayoutState
& aState
,
867 nsRect
rect(aChildBox
->GetRect());
879 nsMargin
margin(0, 0, 0, 0);
880 aChildBox
->GetXULMargin(margin
);
882 RefPtr
<nsAtom
> attribute
;
885 pref
-= (margin
.left
+ margin
.right
);
886 attribute
= nsGkAtoms::width
;
888 pref
-= (margin
.top
+ margin
.bottom
);
889 attribute
= nsGkAtoms::height
;
892 nsIContent
* content
= aChildBox
->GetContent();
893 if (!content
->IsElement()) {
897 // set its preferred size.
898 nsAutoString prefValue
;
899 prefValue
.AppendInt(pref
/ aOnePixel
);
900 if (content
->AsElement()->AttrValueIs(kNameSpaceID_None
, attribute
, prefValue
,
905 AutoWeakFrame
weakBox(aChildBox
);
906 content
->AsElement()->SetAttr(kNameSpaceID_None
, attribute
, prefValue
, true);
907 NS_ENSURE_TRUE_VOID(weakBox
.IsAlive());
908 aState
.PresShell()->FrameNeedsReflow(aChildBox
, nsIPresShell::eStyleChange
,
912 void nsSplitterFrameInner::AddRemoveSpace(nscoord aDiff
,
913 nsSplitterInfo
* aChildInfos
,
914 int32_t aCount
, int32_t& aSpaceLeft
) {
917 for (int i
= 0; i
< aCount
; i
++) {
918 nscoord min
= aChildInfos
[i
].min
;
919 nscoord max
= aChildInfos
[i
].max
;
920 nscoord
& c
= aChildInfos
[i
].changed
;
922 // figure our how much space to add or remove
923 if (c
+ aDiff
< min
) {
926 } else if (c
+ aDiff
> max
) {
934 // there is not space left? We are done
935 if (aDiff
== 0) break;
942 * Ok if we want to resize a child we will know the actual size in pixels we
943 * want it to be. This is not the preferred size. But they only way we can
944 * change a child is my manipulating its preferred size. So give the actual
945 * pixel size this return method will return figure out the preferred size and
949 void nsSplitterFrameInner::ResizeChildTo(nscoord
& aDiff
,
950 nsSplitterInfo
* aChildrenBeforeInfos
,
951 nsSplitterInfo
* aChildrenAfterInfos
,
952 int32_t aChildrenBeforeCount
,
953 int32_t aChildrenAfterCount
,
956 AddRemoveSpace(aDiff
, aChildrenBeforeInfos
, aChildrenBeforeCount
, spaceLeft
);
958 // if there is any space left over remove it from the dif we were originally
961 AddRemoveSpace(-aDiff
, aChildrenAfterInfos
, aChildrenAfterCount
, spaceLeft
);
963 if (spaceLeft
!= 0) {
966 AddRemoveSpace(spaceLeft
, aChildrenBeforeInfos
, aChildrenBeforeCount
,