1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 // Netscape Communications
10 // See documentation in associated header file
13 #include "nsSplitterFrame.h"
14 #include "nsGkAtoms.h"
15 #include "nsIDOMElement.h"
16 #include "nsIDOMXULElement.h"
17 #include "nsPresContext.h"
18 #include "nsRenderingContext.h"
19 #include "nsIDocument.h"
20 #include "nsNameSpaceManager.h"
21 #include "nsScrollbarButtonFrame.h"
22 #include "nsIDOMEventListener.h"
23 #include "nsIDOMMouseEvent.h"
24 #include "nsIPresShell.h"
25 #include "nsFrameList.h"
26 #include "nsHTMLParts.h"
27 #include "nsStyleContext.h"
28 #include "nsBoxLayoutState.h"
29 #include "nsIServiceManager.h"
30 #include "nsContainerFrame.h"
31 #include "nsAutoPtr.h"
32 #include "nsContentCID.h"
33 #include "nsStyleSet.h"
34 #include "nsLayoutUtils.h"
35 #include "nsDisplayList.h"
36 #include "nsContentUtils.h"
37 #include "mozilla/dom/Element.h"
38 #include "mozilla/MouseEvents.h"
40 using namespace mozilla
;
42 class nsSplitterInfo
{
48 nsCOMPtr
<nsIContent
> childElem
;
53 class nsSplitterFrameInner MOZ_FINAL
: public nsIDOMEventListener
56 virtual ~nsSplitterFrameInner();
61 NS_DECL_NSIDOMEVENTLISTENER
63 explicit nsSplitterFrameInner(nsSplitterFrame
* aSplitter
)
69 void Disconnect() { mOuter
= nullptr; }
71 nsresult
MouseDown(nsIDOMEvent
* aMouseEvent
);
72 nsresult
MouseUp(nsIDOMEvent
* aMouseEvent
);
73 nsresult
MouseMove(nsIDOMEvent
* aMouseEvent
);
75 void MouseDrag(nsPresContext
* aPresContext
, WidgetGUIEvent
* aEvent
);
76 void MouseUp(nsPresContext
* aPresContext
, WidgetGUIEvent
* aEvent
);
78 void AdjustChildren(nsPresContext
* aPresContext
);
79 void AdjustChildren(nsPresContext
* aPresContext
, nsSplitterInfo
* aChildInfos
, int32_t aCount
, bool aIsHorizontal
);
81 void AddRemoveSpace(nscoord aDiff
,
82 nsSplitterInfo
* aChildInfos
,
86 void ResizeChildTo(nsPresContext
* aPresContext
,
88 nsSplitterInfo
* aChildrenBeforeInfos
,
89 nsSplitterInfo
* aChildrenAfterInfos
,
90 int32_t aChildrenBeforeCount
,
91 int32_t aChildrenAfterCount
,
96 void AddListener(nsPresContext
* aPresContext
);
97 void RemoveListener();
99 enum ResizeType
{ Closest
, Farthest
, Flex
, Grow
};
100 enum State
{ Open
, CollapsedBefore
, CollapsedAfter
, Dragging
};
101 enum CollapseDirection
{ Before
, After
};
103 ResizeType
GetResizeBefore();
104 ResizeType
GetResizeAfter();
107 void Reverse(nsSplitterInfo
*& aIndexes
, int32_t aCount
);
108 bool SupportsCollapseDirection(CollapseDirection aDirection
);
111 void SetPreferredSize(nsBoxLayoutState
& aState
, nsIFrame
* aChildBox
, nscoord aOnePixel
, bool aIsHorizontal
, nscoord
* aSize
);
113 nsSplitterFrame
* mOuter
;
117 nsIFrame
* mParentBox
;
119 nsSplitterInfo
* mChildInfosBefore
;
120 nsSplitterInfo
* mChildInfosAfter
;
121 int32_t mChildInfosBeforeCount
;
122 int32_t mChildInfosAfterCount
;
124 nscoord mSplitterPos
;
129 NS_IMPL_ISUPPORTS(nsSplitterFrameInner
, nsIDOMEventListener
)
131 nsSplitterFrameInner::ResizeType
132 nsSplitterFrameInner::GetResizeBefore()
134 static nsIContent::AttrValuesArray strings
[] =
135 {&nsGkAtoms::farthest
, &nsGkAtoms::flex
, nullptr};
136 switch (mOuter
->GetContent()->FindAttrValueIn(kNameSpaceID_None
,
137 nsGkAtoms::resizebefore
,
138 strings
, eCaseMatters
)) {
139 case 0: return Farthest
;
145 nsSplitterFrameInner::~nsSplitterFrameInner()
147 delete[] mChildInfosBefore
;
148 delete[] mChildInfosAfter
;
151 nsSplitterFrameInner::ResizeType
152 nsSplitterFrameInner::GetResizeAfter()
154 static nsIContent::AttrValuesArray strings
[] =
155 {&nsGkAtoms::farthest
, &nsGkAtoms::flex
, &nsGkAtoms::grow
, nullptr};
156 switch (mOuter
->GetContent()->FindAttrValueIn(kNameSpaceID_None
,
157 nsGkAtoms::resizeafter
,
158 strings
, eCaseMatters
)) {
159 case 0: return Farthest
;
166 nsSplitterFrameInner::State
167 nsSplitterFrameInner::GetState()
169 static nsIContent::AttrValuesArray strings
[] =
170 {&nsGkAtoms::dragging
, &nsGkAtoms::collapsed
, nullptr};
171 static nsIContent::AttrValuesArray strings_substate
[] =
172 {&nsGkAtoms::before
, &nsGkAtoms::after
, nullptr};
173 switch (mOuter
->GetContent()->FindAttrValueIn(kNameSpaceID_None
,
175 strings
, eCaseMatters
)) {
176 case 0: return Dragging
;
178 switch (mOuter
->GetContent()->FindAttrValueIn(kNameSpaceID_None
,
182 case 0: return CollapsedBefore
;
183 case 1: return CollapsedAfter
;
185 if (SupportsCollapseDirection(After
))
186 return CollapsedAfter
;
187 return CollapsedBefore
;
194 // NS_NewSplitterFrame
196 // Creates a new Toolbar frame and returns it
199 NS_NewSplitterFrame (nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
201 return new (aPresShell
) nsSplitterFrame(aPresShell
, aContext
);
204 NS_IMPL_FRAMEARENA_HELPERS(nsSplitterFrame
)
206 nsSplitterFrame::nsSplitterFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
207 : nsBoxFrame(aPresShell
, aContext
),
213 nsSplitterFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
216 mInner
->RemoveListener();
217 mInner
->Disconnect();
221 nsBoxFrame::DestroyFrom(aDestructRoot
);
226 nsSplitterFrame::GetCursor(const nsPoint
& aPoint
,
227 nsIFrame::Cursor
& aCursor
)
229 return nsBoxFrame::GetCursor(aPoint
, aCursor
);
233 aCursor = NS_STYLE_CURSOR_N_RESIZE;
235 aCursor = NS_STYLE_CURSOR_W_RESIZE;
242 nsSplitterFrame::AttributeChanged(int32_t aNameSpaceID
,
246 nsresult rv
= nsBoxFrame::AttributeChanged(aNameSpaceID
, aAttribute
,
248 // if the alignment changed. Let the grippy know
249 if (aAttribute
== nsGkAtoms::align
) {
250 // tell the slider its attribute changed so it can
252 nsIFrame
* grippy
= nullptr;
253 nsScrollbarButtonFrame::GetChildWithTag(PresContext(), nsGkAtoms::grippy
, this, grippy
);
255 grippy
->AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
256 } else if (aAttribute
== nsGkAtoms::state
) {
257 mInner
->UpdateState();
264 * Initialize us. If we are in a box get our alignment so we know what direction we are
267 nsSplitterFrame::Init(nsIContent
* aContent
,
268 nsContainerFrame
* aParent
,
269 nsIFrame
* aPrevInFlow
)
272 mInner
= new nsSplitterFrameInner(this);
275 mInner
->mChildInfosAfter
= nullptr;
276 mInner
->mChildInfosBefore
= nullptr;
277 mInner
->mState
= nsSplitterFrameInner::Open
;
278 mInner
->mDragging
= false;
280 // determine orientation of parent, and if vertical, set orient to vertical
281 // on splitter content, then re-resolve style
282 // XXXbz this is pretty messed up, since this can change whether we should
283 // have a frame at all. This really needs a better solution.
284 if (aParent
&& aParent
->IsBoxFrame()) {
285 if (!aParent
->IsHorizontal()) {
286 if (!nsContentUtils::HasNonEmptyAttr(aContent
, kNameSpaceID_None
,
287 nsGkAtoms::orient
)) {
288 aContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::orient
,
289 NS_LITERAL_STRING("vertical"), false);
290 nsStyleContext
* parentStyleContext
= StyleContext()->GetParent();
291 nsRefPtr
<nsStyleContext
> newContext
= PresContext()->StyleSet()->
292 ResolveStyleFor(aContent
->AsElement(), parentStyleContext
);
293 SetStyleContextWithoutNotification(newContext
);
298 nsBoxFrame::Init(aContent
, aParent
, aPrevInFlow
);
300 mInner
->mState
= nsSplitterFrameInner::Open
;
301 mInner
->AddListener(PresContext());
302 mInner
->mParentBox
= nullptr;
306 nsSplitterFrame::DoLayout(nsBoxLayoutState
& aState
)
308 if (GetStateBits() & NS_FRAME_FIRST_REFLOW
)
310 mInner
->mParentBox
= nsBox::GetParentBox(this);
311 mInner
->UpdateState();
314 return nsBoxFrame::DoLayout(aState
);
319 nsSplitterFrame::GetInitialOrientation(bool& aIsHorizontal
)
321 nsIFrame
* box
= nsBox::GetParentBox(this);
323 aIsHorizontal
= !box
->IsHorizontal();
326 nsBoxFrame::GetInitialOrientation(aIsHorizontal
);
330 nsSplitterFrame::HandlePress(nsPresContext
* aPresContext
,
331 WidgetGUIEvent
* aEvent
,
332 nsEventStatus
* aEventStatus
)
338 nsSplitterFrame::HandleMultiplePress(nsPresContext
* aPresContext
,
339 WidgetGUIEvent
* aEvent
,
340 nsEventStatus
* aEventStatus
,
347 nsSplitterFrame::HandleDrag(nsPresContext
* aPresContext
,
348 WidgetGUIEvent
* aEvent
,
349 nsEventStatus
* aEventStatus
)
355 nsSplitterFrame::HandleRelease(nsPresContext
* aPresContext
,
356 WidgetGUIEvent
* aEvent
,
357 nsEventStatus
* aEventStatus
)
363 nsSplitterFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
364 const nsRect
& aDirtyRect
,
365 const nsDisplayListSet
& aLists
)
367 nsBoxFrame::BuildDisplayList(aBuilder
, aDirtyRect
, aLists
);
369 // if the mouse is captured always return us as the frame.
370 if (mInner
->mDragging
)
372 // XXX It's probably better not to check visibility here, right?
373 aLists
.Outlines()->AppendNewToTop(new (aBuilder
)
374 nsDisplayEventReceiver(aBuilder
, this));
380 nsSplitterFrame::HandleEvent(nsPresContext
* aPresContext
,
381 WidgetGUIEvent
* aEvent
,
382 nsEventStatus
* aEventStatus
)
384 NS_ENSURE_ARG_POINTER(aEventStatus
);
385 if (nsEventStatus_eConsumeNoDefault
== *aEventStatus
) {
389 nsWeakFrame
weakFrame(this);
390 nsRefPtr
<nsSplitterFrameInner
> kungFuDeathGrip(mInner
);
391 switch (aEvent
->message
) {
393 mInner
->MouseDrag(aPresContext
, aEvent
);
396 case NS_MOUSE_BUTTON_UP
:
397 if (aEvent
->AsMouseEvent()->button
== WidgetMouseEvent::eLeftButton
) {
398 mInner
->MouseUp(aPresContext
, aEvent
);
403 NS_ENSURE_STATE(weakFrame
.IsAlive());
404 return nsBoxFrame::HandleEvent(aPresContext
, aEvent
, aEventStatus
);
408 nsSplitterFrameInner::MouseUp(nsPresContext
* aPresContext
,
409 WidgetGUIEvent
* aEvent
)
411 if (mDragging
&& mOuter
) {
412 AdjustChildren(aPresContext
);
413 AddListener(aPresContext
);
414 nsIPresShell::SetCapturingContent(nullptr, 0); // XXXndeakin is this needed?
416 State newState
= GetState();
417 // if the state is dragging then make it Open.
418 if (newState
== Dragging
)
419 mOuter
->mContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::state
, EmptyString(), true);
423 // if we dragged then fire a command event.
425 nsCOMPtr
<nsIDOMXULElement
> element
= do_QueryInterface(mOuter
->GetContent());
426 element
->DoCommand();
429 //printf("MouseUp\n");
432 delete[] mChildInfosBefore
;
433 delete[] mChildInfosAfter
;
434 mChildInfosBefore
= nullptr;
435 mChildInfosAfter
= nullptr;
436 mChildInfosBeforeCount
= 0;
437 mChildInfosAfterCount
= 0;
441 nsSplitterFrameInner::MouseDrag(nsPresContext
* aPresContext
,
442 WidgetGUIEvent
* aEvent
)
444 if (mDragging
&& mOuter
) {
446 //printf("Dragging\n");
448 bool isHorizontal
= !mOuter
->IsHorizontal();
449 // convert coord to pixels
450 nsPoint pt
= nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
,
452 nscoord pos
= isHorizontal
? pt
.x
: pt
.y
;
454 // mDragStart is in frame coordinates
455 nscoord start
= mDragStart
;
457 // take our current position and subtract the start location
460 //printf("Diff=%d\n", pos);
462 ResizeType resizeAfter
= GetResizeAfter();
466 if (resizeAfter
== nsSplitterFrameInner::Grow
)
472 for (i
=0; i
< mChildInfosBeforeCount
; i
++)
473 mChildInfosBefore
[i
].changed
= mChildInfosBefore
[i
].current
;
475 for (i
=0; i
< mChildInfosAfterCount
; i
++)
476 mChildInfosAfter
[i
].changed
= mChildInfosAfter
[i
].current
;
478 nscoord oldPos
= pos
;
480 ResizeChildTo(aPresContext
, pos
, mChildInfosBefore
, mChildInfosAfter
, mChildInfosBeforeCount
, mChildInfosAfterCount
, bounded
);
482 State currentState
= GetState();
483 bool supportsBefore
= SupportsCollapseDirection(Before
);
484 bool supportsAfter
= SupportsCollapseDirection(After
);
486 const bool isRTL
= mOuter
->StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
;
487 bool pastEnd
= oldPos
> 0 && oldPos
> pos
;
488 bool pastBegin
= oldPos
< 0 && oldPos
< pos
;
490 // Swap the boundary checks in RTL mode
495 const bool isCollapsedBefore
= pastBegin
&& supportsBefore
;
496 const bool isCollapsedAfter
= pastEnd
&& supportsAfter
;
498 // if we are in a collapsed position
499 if (isCollapsedBefore
|| isCollapsedAfter
)
501 // and we are not collapsed then collapse
502 if (currentState
== Dragging
) {
505 //printf("Collapse right\n");
508 nsCOMPtr
<nsIContent
> outer
= mOuter
->mContent
;
509 outer
->SetAttr(kNameSpaceID_None
, nsGkAtoms::substate
,
510 NS_LITERAL_STRING("after"),
512 outer
->SetAttr(kNameSpaceID_None
, nsGkAtoms::state
,
513 NS_LITERAL_STRING("collapsed"),
517 } else if (pastBegin
)
519 //printf("Collapse left\n");
522 nsCOMPtr
<nsIContent
> outer
= mOuter
->mContent
;
523 outer
->SetAttr(kNameSpaceID_None
, nsGkAtoms::substate
,
524 NS_LITERAL_STRING("before"),
526 outer
->SetAttr(kNameSpaceID_None
, nsGkAtoms::state
,
527 NS_LITERAL_STRING("collapsed"),
533 // if we are not in a collapsed position and we are not dragging make sure
535 if (currentState
!= Dragging
)
536 mOuter
->mContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::state
, NS_LITERAL_STRING("dragging"), true);
537 AdjustChildren(aPresContext
);
545 nsSplitterFrameInner::AddListener(nsPresContext
* aPresContext
)
547 mOuter
->GetContent()->
548 AddEventListener(NS_LITERAL_STRING("mouseup"), this, false, false);
549 mOuter
->GetContent()->
550 AddEventListener(NS_LITERAL_STRING("mousedown"), this, false, false);
551 mOuter
->GetContent()->
552 AddEventListener(NS_LITERAL_STRING("mousemove"), this, false, false);
553 mOuter
->GetContent()->
554 AddEventListener(NS_LITERAL_STRING("mouseout"), this, false, false);
558 nsSplitterFrameInner::RemoveListener()
561 mOuter
->GetContent()->
562 RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, false);
563 mOuter
->GetContent()->
564 RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, false);
565 mOuter
->GetContent()->
566 RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, false);
567 mOuter
->GetContent()->
568 RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, false);
572 nsSplitterFrameInner::HandleEvent(nsIDOMEvent
* aEvent
)
574 nsAutoString eventType
;
575 aEvent
->GetType(eventType
);
576 if (eventType
.EqualsLiteral("mouseup"))
577 return MouseUp(aEvent
);
578 if (eventType
.EqualsLiteral("mousedown"))
579 return MouseDown(aEvent
);
580 if (eventType
.EqualsLiteral("mousemove") ||
581 eventType
.EqualsLiteral("mouseout"))
582 return MouseMove(aEvent
);
589 nsSplitterFrameInner::MouseUp(nsIDOMEvent
* aMouseEvent
)
591 NS_ENSURE_TRUE(mOuter
, NS_OK
);
594 nsIPresShell::SetCapturingContent(nullptr, 0);
600 nsSplitterFrameInner::MouseDown(nsIDOMEvent
* aMouseEvent
)
602 NS_ENSURE_TRUE(mOuter
, NS_OK
);
603 nsCOMPtr
<nsIDOMMouseEvent
> mouseEvent(do_QueryInterface(aMouseEvent
));
608 mouseEvent
->GetButton(&button
);
610 // only if left button
614 if (mOuter
->GetContent()->
615 AttrValueIs(kNameSpaceID_None
, nsGkAtoms::disabled
,
616 nsGkAtoms::_true
, eCaseMatters
))
619 mParentBox
= nsBox::GetParentBox(mOuter
);
624 nsPresContext
* outerPresContext
= mOuter
->PresContext();
625 const nsFrameList
& siblingList(mParentBox
->PrincipalChildList());
626 int32_t childIndex
= siblingList
.IndexOf(mOuter
);
627 // if it's 0 (or not found) then stop right here.
628 // It might be not found if we're not in the parent's primary frame list.
632 int32_t childCount
= siblingList
.GetLength();
633 // if it's the last index then we need to allow for resizeafter="grow"
634 if (childIndex
== childCount
- 1 && GetResizeAfter() != Grow
)
637 nsRefPtr
<nsRenderingContext
> rc
=
638 outerPresContext
->PresShell()->CreateReferenceRenderingContext();
639 nsBoxLayoutState
state(outerPresContext
, rc
);
646 bool isHorizontal
= !mOuter
->IsHorizontal();
648 ResizeType resizeBefore
= GetResizeBefore();
649 ResizeType resizeAfter
= GetResizeAfter();
651 delete[] mChildInfosBefore
;
652 delete[] mChildInfosAfter
;
653 mChildInfosBefore
= new nsSplitterInfo
[childCount
];
654 mChildInfosAfter
= new nsSplitterInfo
[childCount
];
656 // create info 2 lists. One of the children before us and one after.
658 mChildInfosBeforeCount
= 0;
659 mChildInfosAfterCount
= 0;
661 nsIFrame
* childBox
= nsBox::GetChildBox(mParentBox
);
663 while (nullptr != childBox
)
665 nsIContent
* content
= childBox
->GetContent();
666 nsIDocument
* doc
= content
->OwnerDoc();
668 nsIAtom
* atom
= doc
->BindingManager()->ResolveTag(content
, &dummy
);
670 // skip over any splitters
671 if (atom
!= nsGkAtoms::splitter
) {
672 nsSize prefSize
= childBox
->GetPrefSize(state
);
673 nsSize minSize
= childBox
->GetMinSize(state
);
674 nsSize maxSize
= nsBox::BoundsCheckMinMax(minSize
, childBox
->GetMaxSize(state
));
675 prefSize
= nsBox::BoundsCheck(minSize
, prefSize
, maxSize
);
677 mOuter
->AddMargin(childBox
, minSize
);
678 mOuter
->AddMargin(childBox
, prefSize
);
679 mOuter
->AddMargin(childBox
, maxSize
);
681 nscoord flex
= childBox
->GetFlex(state
);
683 nsMargin
margin(0,0,0,0);
684 childBox
->GetMargin(margin
);
685 nsRect
r(childBox
->GetRect());
688 // We need to check for hidden attribute too, since treecols with
689 // the hidden="true" attribute are not really hidden, just collapsed
690 if (!content
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::fixed
,
691 nsGkAtoms::_true
, eCaseMatters
) &&
692 !content
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::hidden
,
693 nsGkAtoms::_true
, eCaseMatters
)) {
694 if (count
< childIndex
&& (resizeBefore
!= Flex
|| flex
> 0)) {
695 mChildInfosBefore
[mChildInfosBeforeCount
].childElem
= content
;
696 mChildInfosBefore
[mChildInfosBeforeCount
].min
= isHorizontal
? minSize
.width
: minSize
.height
;
697 mChildInfosBefore
[mChildInfosBeforeCount
].max
= isHorizontal
? maxSize
.width
: maxSize
.height
;
698 mChildInfosBefore
[mChildInfosBeforeCount
].current
= isHorizontal
? r
.width
: r
.height
;
699 mChildInfosBefore
[mChildInfosBeforeCount
].flex
= flex
;
700 mChildInfosBefore
[mChildInfosBeforeCount
].index
= count
;
701 mChildInfosBefore
[mChildInfosBeforeCount
].changed
= mChildInfosBefore
[mChildInfosBeforeCount
].current
;
702 mChildInfosBeforeCount
++;
703 } else if (count
> childIndex
&& (resizeAfter
!= Flex
|| flex
> 0)) {
704 mChildInfosAfter
[mChildInfosAfterCount
].childElem
= content
;
705 mChildInfosAfter
[mChildInfosAfterCount
].min
= isHorizontal
? minSize
.width
: minSize
.height
;
706 mChildInfosAfter
[mChildInfosAfterCount
].max
= isHorizontal
? maxSize
.width
: maxSize
.height
;
707 mChildInfosAfter
[mChildInfosAfterCount
].current
= isHorizontal
? r
.width
: r
.height
;
708 mChildInfosAfter
[mChildInfosAfterCount
].flex
= flex
;
709 mChildInfosAfter
[mChildInfosAfterCount
].index
= count
;
710 mChildInfosAfter
[mChildInfosAfterCount
].changed
= mChildInfosAfter
[mChildInfosAfterCount
].current
;
711 mChildInfosAfterCount
++;
716 childBox
= nsBox::GetNextBox(childBox
);
720 if (!mParentBox
->IsNormalDirection()) {
721 // The before array is really the after array, and the order needs to be reversed.
722 // First reverse both arrays.
723 Reverse(mChildInfosBefore
, mChildInfosBeforeCount
);
724 Reverse(mChildInfosAfter
, mChildInfosAfterCount
);
726 // Now swap the two arrays.
727 nscoord newAfterCount
= mChildInfosBeforeCount
;
728 mChildInfosBeforeCount
= mChildInfosAfterCount
;
729 mChildInfosAfterCount
= newAfterCount
;
730 nsSplitterInfo
* temp
= mChildInfosAfter
;
731 mChildInfosAfter
= mChildInfosBefore
;
732 mChildInfosBefore
= temp
;
735 // if resizebefore is not Farthest, reverse the list because the first child
736 // in the list is the farthest, and we want the first child to be the closest.
737 if (resizeBefore
!= Farthest
)
738 Reverse(mChildInfosBefore
, mChildInfosBeforeCount
);
740 // if the resizeafter is the Farthest we must reverse the list because the first child in the list
741 // is the closest we want the first child to be the Farthest.
742 if (resizeAfter
== Farthest
)
743 Reverse(mChildInfosAfter
, mChildInfosAfterCount
);
745 // grow only applys to the children after. If grow is set then no space should be taken out of any children after
746 // us. To do this we just set the size of that list to be 0.
747 if (resizeAfter
== Grow
)
748 mChildInfosAfterCount
= 0;
751 nsPoint pt
= nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(mouseEvent
,
755 mSplitterPos
= mOuter
->mRect
.x
;
758 mSplitterPos
= mOuter
->mRect
.y
;
763 //printf("Pressed mDragStart=%d\n",mDragStart);
765 nsIPresShell::SetCapturingContent(mOuter
->GetContent(), CAPTURE_IGNOREALLOWED
);
771 nsSplitterFrameInner::MouseMove(nsIDOMEvent
* aMouseEvent
)
773 NS_ENSURE_TRUE(mOuter
, NS_OK
);
780 nsCOMPtr
<nsIDOMEventListener
> kungfuDeathGrip(this);
781 mOuter
->mContent
->SetAttr(kNameSpaceID_None
, nsGkAtoms::state
,
782 NS_LITERAL_STRING("dragging"), true);
791 nsSplitterFrameInner::Reverse(nsSplitterInfo
*& aChildInfos
, int32_t aCount
)
793 nsSplitterInfo
* infos
= new nsSplitterInfo
[aCount
];
795 for (int i
=0; i
< aCount
; i
++)
796 infos
[i
] = aChildInfos
[aCount
- 1 - i
];
798 delete[] aChildInfos
;
803 nsSplitterFrameInner::SupportsCollapseDirection
805 nsSplitterFrameInner::CollapseDirection aDirection
808 static nsIContent::AttrValuesArray strings
[] =
809 {&nsGkAtoms::before
, &nsGkAtoms::after
, &nsGkAtoms::both
, nullptr};
811 switch (mOuter
->mContent
->FindAttrValueIn(kNameSpaceID_None
,
813 strings
, eCaseMatters
)) {
815 return (aDirection
== Before
);
817 return (aDirection
== After
);
826 nsSplitterFrameInner::UpdateState()
828 // State Transitions:
830 // Open -> CollapsedBefore
831 // Open -> CollapsedAfter
832 // CollapsedBefore -> Open
833 // CollapsedBefore -> Dragging
834 // CollapsedAfter -> Open
835 // CollapsedAfter -> Dragging
837 // Dragging -> CollapsedBefore (auto collapse)
838 // Dragging -> CollapsedAfter (auto collapse)
840 State newState
= GetState();
842 if (newState
== mState
) {
847 if ((SupportsCollapseDirection(Before
) || SupportsCollapseDirection(After
)) &&
848 mOuter
->GetParent()->IsBoxFrame()) {
849 // Find the splitter's immediate sibling.
850 nsIFrame
* splitterSibling
;
851 if (newState
== CollapsedBefore
|| mState
== CollapsedBefore
) {
852 splitterSibling
= mOuter
->GetPrevSibling();
854 splitterSibling
= mOuter
->GetNextSibling();
857 if (splitterSibling
) {
858 nsCOMPtr
<nsIContent
> sibling
= splitterSibling
->GetContent();
860 if (mState
== CollapsedBefore
|| mState
== CollapsedAfter
) {
861 // CollapsedBefore -> Open
862 // CollapsedBefore -> Dragging
863 // CollapsedAfter -> Open
864 // CollapsedAfter -> Dragging
865 nsContentUtils::AddScriptRunner(
866 new nsUnsetAttrRunnable(sibling
, nsGkAtoms::collapsed
));
867 } else if ((mState
== Open
|| mState
== Dragging
)
868 && (newState
== CollapsedBefore
||
869 newState
== CollapsedAfter
)) {
870 // Open -> CollapsedBefore / CollapsedAfter
871 // Dragging -> CollapsedBefore / CollapsedAfter
872 nsContentUtils::AddScriptRunner(
873 new nsSetAttrRunnable(sibling
, nsGkAtoms::collapsed
,
874 NS_LITERAL_STRING("true")));
883 nsSplitterFrameInner::EnsureOrient()
885 bool isHorizontal
= !(mParentBox
->GetStateBits() & NS_STATE_IS_HORIZONTAL
);
887 mOuter
->mState
|= NS_STATE_IS_HORIZONTAL
;
889 mOuter
->mState
&= ~NS_STATE_IS_HORIZONTAL
;
893 nsSplitterFrameInner::AdjustChildren(nsPresContext
* aPresContext
)
896 bool isHorizontal
= !mOuter
->IsHorizontal();
898 AdjustChildren(aPresContext
, mChildInfosBefore
, mChildInfosBeforeCount
, isHorizontal
);
899 AdjustChildren(aPresContext
, mChildInfosAfter
, mChildInfosAfterCount
, isHorizontal
);
902 static nsIFrame
* GetChildBoxForContent(nsIFrame
* aParentBox
, nsIContent
* aContent
)
904 nsIFrame
* childBox
= nsBox::GetChildBox(aParentBox
);
906 while (nullptr != childBox
) {
907 if (childBox
->GetContent() == aContent
) {
910 childBox
= nsBox::GetNextBox(childBox
);
916 nsSplitterFrameInner::AdjustChildren(nsPresContext
* aPresContext
, nsSplitterInfo
* aChildInfos
, int32_t aCount
, bool aIsHorizontal
)
918 ///printf("------- AdjustChildren------\n");
920 nsBoxLayoutState
state(aPresContext
);
922 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
924 // first set all the widths.
925 nsIFrame
* child
= nsBox::GetChildBox(mOuter
);
928 SetPreferredSize(state
, child
, onePixel
, aIsHorizontal
, nullptr);
929 child
= nsBox::GetNextBox(child
);
932 // now set our changed widths.
933 for (int i
=0; i
< aCount
; i
++)
935 nscoord pref
= aChildInfos
[i
].changed
;
936 nsIFrame
* childBox
= GetChildBoxForContent(mParentBox
, aChildInfos
[i
].childElem
);
939 SetPreferredSize(state
, childBox
, onePixel
, aIsHorizontal
, &pref
);
945 nsSplitterFrameInner::SetPreferredSize(nsBoxLayoutState
& aState
, nsIFrame
* aChildBox
, nscoord aOnePixel
, bool aIsHorizontal
, nscoord
* aSize
)
947 nsRect
rect(aChildBox
->GetRect());
960 nsMargin
margin(0,0,0,0);
961 aChildBox
->GetMargin(margin
);
963 nsCOMPtr
<nsIAtom
> attribute
;
966 pref
-= (margin
.left
+ margin
.right
);
967 attribute
= nsGkAtoms::width
;
969 pref
-= (margin
.top
+ margin
.bottom
);
970 attribute
= nsGkAtoms::height
;
973 nsIContent
* content
= aChildBox
->GetContent();
975 // set its preferred size.
976 nsAutoString prefValue
;
977 prefValue
.AppendInt(pref
/aOnePixel
);
978 if (content
->AttrValueIs(kNameSpaceID_None
, attribute
,
979 prefValue
, eCaseMatters
))
982 nsWeakFrame
weakBox(aChildBox
);
983 content
->SetAttr(kNameSpaceID_None
, attribute
, prefValue
, true);
984 ENSURE_TRUE(weakBox
.IsAlive());
985 aState
.PresShell()->FrameNeedsReflow(aChildBox
, nsIPresShell::eStyleChange
,
991 nsSplitterFrameInner::AddRemoveSpace(nscoord aDiff
,
992 nsSplitterInfo
* aChildInfos
,
998 for (int i
=0; i
< aCount
; i
++) {
999 nscoord min
= aChildInfos
[i
].min
;
1000 nscoord max
= aChildInfos
[i
].max
;
1001 nscoord
& c
= aChildInfos
[i
].changed
;
1003 // figure our how much space to add or remove
1004 if (c
+ aDiff
< min
) {
1007 } else if (c
+ aDiff
> max
) {
1015 // there is not space left? We are done
1024 * Ok if we want to resize a child we will know the actual size in pixels we want it to be.
1025 * This is not the preferred size. But they only way we can change a child is my manipulating its
1026 * preferred size. So give the actual pixel size this return method will return figure out the preferred
1031 nsSplitterFrameInner::ResizeChildTo(nsPresContext
* aPresContext
,
1033 nsSplitterInfo
* aChildrenBeforeInfos
,
1034 nsSplitterInfo
* aChildrenAfterInfos
,
1035 int32_t aChildrenBeforeCount
,
1036 int32_t aChildrenAfterCount
,
1040 AddRemoveSpace(aDiff
, aChildrenBeforeInfos
,aChildrenBeforeCount
,spaceLeft
);
1042 // if there is any space left over remove it from the dif we were originally given
1044 AddRemoveSpace(-aDiff
, aChildrenAfterInfos
,aChildrenAfterCount
,spaceLeft
);
1046 if (spaceLeft
!= 0) {
1049 AddRemoveSpace(spaceLeft
, aChildrenBeforeInfos
,aChildrenBeforeCount
,spaceLeft
);