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 "mozilla/dom/Document.h"
20 #include "nsNameSpaceManager.h"
21 #include "nsScrollbarButtonFrame.h"
22 #include "nsIDOMEventListener.h"
23 #include "nsFrameList.h"
24 #include "nsHTMLParts.h"
25 #include "mozilla/ComputedStyle.h"
26 #include "mozilla/CSSOrderAwareFrameIterator.h"
27 #include "nsBoxLayoutState.h"
28 #include "nsContainerFrame.h"
29 #include "nsContentCID.h"
30 #include "nsLayoutUtils.h"
31 #include "nsDisplayList.h"
32 #include "nsContentUtils.h"
33 #include "mozilla/dom/Element.h"
34 #include "mozilla/dom/Event.h"
35 #include "mozilla/dom/MouseEvent.h"
36 #include "mozilla/MouseEvents.h"
37 #include "mozilla/PresShell.h"
38 #include "mozilla/UniquePtr.h"
40 using namespace mozilla
;
42 using mozilla::dom::Element
;
43 using mozilla::dom::Event
;
45 class nsSplitterInfo
{
51 nsCOMPtr
<nsIContent
> childElem
;
55 class nsSplitterFrameInner final
: public nsIDOMEventListener
{
57 virtual ~nsSplitterFrameInner();
61 NS_DECL_NSIDOMEVENTLISTENER
63 explicit nsSplitterFrameInner(nsSplitterFrame
* aSplitter
)
67 mChildInfosBeforeCount(0),
68 mChildInfosAfterCount(0),
76 void Disconnect() { mOuter
= nullptr; }
78 nsresult
MouseDown(Event
* aMouseEvent
);
79 nsresult
MouseUp(Event
* aMouseEvent
);
80 nsresult
MouseMove(Event
* aMouseEvent
);
82 void MouseDrag(nsPresContext
* aPresContext
, WidgetGUIEvent
* aEvent
);
83 void MouseUp(nsPresContext
* aPresContext
, WidgetGUIEvent
* aEvent
);
85 void AdjustChildren(nsPresContext
* aPresContext
);
86 void AdjustChildren(nsPresContext
* aPresContext
, nsSplitterInfo
* aChildInfos
,
87 int32_t aCount
, bool aIsHorizontal
);
89 void AddRemoveSpace(nscoord aDiff
, nsSplitterInfo
* aChildInfos
,
90 int32_t aCount
, int32_t& aSpaceLeft
);
92 void ResizeChildTo(nscoord
& aDiff
, nsSplitterInfo
* aChildrenBeforeInfos
,
93 nsSplitterInfo
* aChildrenAfterInfos
,
94 int32_t aChildrenBeforeCount
, int32_t aChildrenAfterCount
,
100 void RemoveListener();
102 enum ResizeType
{ Closest
, Farthest
, Flex
, Grow
};
103 enum State
{ Open
, CollapsedBefore
, CollapsedAfter
, Dragging
};
104 enum CollapseDirection
{ Before
, After
};
106 ResizeType
GetResizeBefore();
107 ResizeType
GetResizeAfter();
110 void Reverse(UniquePtr
<nsSplitterInfo
[]>& aIndexes
, int32_t aCount
);
111 bool SupportsCollapseDirection(CollapseDirection aDirection
);
114 void SetPreferredSize(nsBoxLayoutState
& aState
, nsIFrame
* aChildBox
,
115 nscoord aOnePixel
, bool aIsHorizontal
, nscoord
* aSize
);
117 nsSplitterFrame
* mOuter
;
120 nsIFrame
* mParentBox
;
122 UniquePtr
<nsSplitterInfo
[]> mChildInfosBefore
;
123 UniquePtr
<nsSplitterInfo
[]> mChildInfosAfter
;
124 int32_t mChildInfosBeforeCount
;
125 int32_t mChildInfosAfterCount
;
127 nscoord mSplitterPos
;
130 const Element
* SplitterElement() const {
131 return mOuter
->GetContent()->AsElement();
135 NS_IMPL_ISUPPORTS(nsSplitterFrameInner
, nsIDOMEventListener
)
137 nsSplitterFrameInner::ResizeType
nsSplitterFrameInner::GetResizeBefore() {
138 static Element::AttrValuesArray strings
[] = {nsGkAtoms::farthest
,
139 nsGkAtoms::flex
, nullptr};
140 switch (SplitterElement()->FindAttrValueIn(
141 kNameSpaceID_None
, nsGkAtoms::resizebefore
, strings
, eCaseMatters
)) {
150 nsSplitterFrameInner::~nsSplitterFrameInner() = default;
152 nsSplitterFrameInner::ResizeType
nsSplitterFrameInner::GetResizeAfter() {
153 static Element::AttrValuesArray strings
[] = {
154 nsGkAtoms::farthest
, nsGkAtoms::flex
, nsGkAtoms::grow
, nullptr};
155 switch (SplitterElement()->FindAttrValueIn(
156 kNameSpaceID_None
, nsGkAtoms::resizeafter
, strings
, eCaseMatters
)) {
167 nsSplitterFrameInner::State
nsSplitterFrameInner::GetState() {
168 static Element::AttrValuesArray strings
[] = {nsGkAtoms::dragging
,
169 nsGkAtoms::collapsed
, nullptr};
170 static Element::AttrValuesArray strings_substate
[] = {
171 nsGkAtoms::before
, nsGkAtoms::after
, nullptr};
172 switch (SplitterElement()->FindAttrValueIn(
173 kNameSpaceID_None
, nsGkAtoms::state
, strings
, eCaseMatters
)) {
177 switch (SplitterElement()->FindAttrValueIn(
178 kNameSpaceID_None
, nsGkAtoms::substate
, strings_substate
,
181 return CollapsedBefore
;
183 return CollapsedAfter
;
185 if (SupportsCollapseDirection(After
)) return CollapsedAfter
;
186 return CollapsedBefore
;
193 // NS_NewSplitterFrame
195 // Creates a new Toolbar frame and returns it
197 nsIFrame
* NS_NewSplitterFrame(PresShell
* aPresShell
, ComputedStyle
* aStyle
) {
198 return new (aPresShell
) nsSplitterFrame(aStyle
, aPresShell
->GetPresContext());
201 NS_IMPL_FRAMEARENA_HELPERS(nsSplitterFrame
)
203 nsSplitterFrame::nsSplitterFrame(ComputedStyle
* aStyle
,
204 nsPresContext
* aPresContext
)
205 : nsBoxFrame(aStyle
, aPresContext
, 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::AttributeChanged(int32_t aNameSpaceID
,
222 nsBoxFrame::AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
223 if (aAttribute
== nsGkAtoms::state
) {
224 mInner
->UpdateState();
231 * Initialize us. If we are in a box get our alignment so we know what direction
234 void nsSplitterFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
235 nsIFrame
* aPrevInFlow
) {
237 mInner
= new nsSplitterFrameInner(this);
241 // determine orientation of parent, and if vertical, set orient to vertical
242 // on splitter content, then re-resolve style
243 // XXXbz this is pretty messed up, since this can change whether we should
244 // have a frame at all. This really needs a better solution.
245 if (aParent
&& aParent
->IsXULBoxFrame()) {
246 if (!aParent
->IsXULHorizontal()) {
247 if (!nsContentUtils::HasNonEmptyAttr(aContent
, kNameSpaceID_None
,
248 nsGkAtoms::orient
)) {
249 aContent
->AsElement()->SetAttr(kNameSpaceID_None
, nsGkAtoms::orient
,
250 u
"vertical"_ns
, false);
255 nsBoxFrame::Init(aContent
, aParent
, aPrevInFlow
);
257 mInner
->mState
= nsSplitterFrameInner::Open
;
258 mInner
->AddListener();
259 mInner
->mParentBox
= nullptr;
263 nsSplitterFrame::DoXULLayout(nsBoxLayoutState
& aState
) {
264 if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW
)) {
265 mInner
->mParentBox
= nsIFrame::GetParentXULBox(this);
266 mInner
->UpdateState();
269 return nsBoxFrame::DoXULLayout(aState
);
272 void nsSplitterFrame::GetInitialOrientation(bool& aIsHorizontal
) {
273 nsIFrame
* box
= nsIFrame::GetParentXULBox(this);
275 aIsHorizontal
= !box
->IsXULHorizontal();
277 nsBoxFrame::GetInitialOrientation(aIsHorizontal
);
281 nsSplitterFrame::HandlePress(nsPresContext
* aPresContext
,
282 WidgetGUIEvent
* aEvent
,
283 nsEventStatus
* aEventStatus
) {
288 nsSplitterFrame::HandleMultiplePress(nsPresContext
* aPresContext
,
289 WidgetGUIEvent
* aEvent
,
290 nsEventStatus
* aEventStatus
,
296 nsSplitterFrame::HandleDrag(nsPresContext
* aPresContext
, WidgetGUIEvent
* aEvent
,
297 nsEventStatus
* aEventStatus
) {
302 nsSplitterFrame::HandleRelease(nsPresContext
* aPresContext
,
303 WidgetGUIEvent
* aEvent
,
304 nsEventStatus
* aEventStatus
) {
308 void nsSplitterFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
309 const nsDisplayListSet
& aLists
) {
310 nsBoxFrame::BuildDisplayList(aBuilder
, aLists
);
312 // if the mouse is captured always return us as the frame.
313 if (mInner
->mDragging
&& aBuilder
->IsForEventDelivery()) {
314 // XXX It's probably better not to check visibility here, right?
315 aLists
.Outlines()->AppendNewToTop
<nsDisplayEventReceiver
>(aBuilder
, this);
320 nsresult
nsSplitterFrame::HandleEvent(nsPresContext
* aPresContext
,
321 WidgetGUIEvent
* aEvent
,
322 nsEventStatus
* aEventStatus
) {
323 NS_ENSURE_ARG_POINTER(aEventStatus
);
324 if (nsEventStatus_eConsumeNoDefault
== *aEventStatus
) {
328 AutoWeakFrame
weakFrame(this);
329 RefPtr
<nsSplitterFrameInner
> inner(mInner
);
330 switch (aEvent
->mMessage
) {
332 inner
->MouseDrag(aPresContext
, aEvent
);
336 if (aEvent
->AsMouseEvent()->mButton
== MouseButton::ePrimary
) {
337 inner
->MouseUp(aPresContext
, aEvent
);
345 NS_ENSURE_STATE(weakFrame
.IsAlive());
346 return nsBoxFrame::HandleEvent(aPresContext
, aEvent
, aEventStatus
);
349 void nsSplitterFrameInner::MouseUp(nsPresContext
* aPresContext
,
350 WidgetGUIEvent
* aEvent
) {
351 if (mDragging
&& mOuter
) {
352 AdjustChildren(aPresContext
);
354 PresShell::ReleaseCapturingContent(); // XXXndeakin is this needed?
356 State newState
= GetState();
357 // if the state is dragging then make it Open.
358 if (newState
== Dragging
) {
359 mOuter
->mContent
->AsElement()->SetAttr(kNameSpaceID_None
,
360 nsGkAtoms::state
, u
""_ns
, true);
365 // if we dragged then fire a command event.
367 RefPtr
<nsXULElement
> element
=
368 nsXULElement::FromNode(mOuter
->GetContent());
369 element
->DoCommand();
372 // printf("MouseUp\n");
375 mChildInfosBefore
= nullptr;
376 mChildInfosAfter
= nullptr;
377 mChildInfosBeforeCount
= 0;
378 mChildInfosAfterCount
= 0;
381 void nsSplitterFrameInner::MouseDrag(nsPresContext
* aPresContext
,
382 WidgetGUIEvent
* aEvent
) {
383 if (mDragging
&& mOuter
) {
384 // printf("Dragging\n");
386 bool isHorizontal
= !mOuter
->IsXULHorizontal();
387 // convert coord to pixels
388 nsPoint pt
= nsLayoutUtils::GetEventCoordinatesRelativeTo(
389 aEvent
, RelativeTo
{mParentBox
});
390 nscoord pos
= isHorizontal
? pt
.x
: pt
.y
;
392 // mDragStart is in frame coordinates
393 nscoord start
= mDragStart
;
395 // take our current position and subtract the start location
398 // printf("Diff=%d\n", pos);
400 ResizeType resizeAfter
= GetResizeAfter();
402 const bool bounded
= resizeAfter
!= nsSplitterFrameInner::Grow
;
405 for (i
= 0; i
< mChildInfosBeforeCount
; i
++)
406 mChildInfosBefore
[i
].changed
= mChildInfosBefore
[i
].current
;
408 for (i
= 0; i
< mChildInfosAfterCount
; i
++)
409 mChildInfosAfter
[i
].changed
= mChildInfosAfter
[i
].current
;
411 nscoord oldPos
= pos
;
413 ResizeChildTo(pos
, mChildInfosBefore
.get(), mChildInfosAfter
.get(),
414 mChildInfosBeforeCount
, mChildInfosAfterCount
, bounded
);
416 State currentState
= GetState();
417 bool supportsBefore
= SupportsCollapseDirection(Before
);
418 bool supportsAfter
= SupportsCollapseDirection(After
);
421 mOuter
->StyleVisibility()->mDirection
== StyleDirection::Rtl
;
422 bool pastEnd
= oldPos
> 0 && oldPos
> pos
;
423 bool pastBegin
= oldPos
< 0 && oldPos
< pos
;
425 // Swap the boundary checks in RTL mode
426 std::swap(pastEnd
, pastBegin
);
428 const bool isCollapsedBefore
= pastBegin
&& supportsBefore
;
429 const bool isCollapsedAfter
= pastEnd
&& supportsAfter
;
431 // if we are in a collapsed position
432 if (isCollapsedBefore
|| isCollapsedAfter
) {
433 // and we are not collapsed then collapse
434 if (currentState
== Dragging
) {
436 // printf("Collapse right\n");
438 RefPtr
<Element
> outer
= mOuter
->mContent
->AsElement();
439 outer
->SetAttr(kNameSpaceID_None
, nsGkAtoms::substate
, u
"after"_ns
,
441 outer
->SetAttr(kNameSpaceID_None
, nsGkAtoms::state
, u
"collapsed"_ns
,
445 } else if (pastBegin
) {
446 // printf("Collapse left\n");
447 if (supportsBefore
) {
448 RefPtr
<Element
> outer
= mOuter
->mContent
->AsElement();
449 outer
->SetAttr(kNameSpaceID_None
, nsGkAtoms::substate
, u
"before"_ns
,
451 outer
->SetAttr(kNameSpaceID_None
, nsGkAtoms::state
, u
"collapsed"_ns
,
457 // if we are not in a collapsed position and we are not dragging make sure
459 if (currentState
!= Dragging
) {
460 mOuter
->mContent
->AsElement()->SetAttr(
461 kNameSpaceID_None
, nsGkAtoms::state
, u
"dragging"_ns
, true);
463 AdjustChildren(aPresContext
);
470 void nsSplitterFrameInner::AddListener() {
471 mOuter
->GetContent()->AddEventListener(u
"mouseup"_ns
, this, false, false);
472 mOuter
->GetContent()->AddEventListener(u
"mousedown"_ns
, this, false, false);
473 mOuter
->GetContent()->AddEventListener(u
"mousemove"_ns
, this, false, false);
474 mOuter
->GetContent()->AddEventListener(u
"mouseout"_ns
, this, false, false);
477 void nsSplitterFrameInner::RemoveListener() {
478 NS_ENSURE_TRUE_VOID(mOuter
);
479 mOuter
->GetContent()->RemoveEventListener(u
"mouseup"_ns
, this, false);
480 mOuter
->GetContent()->RemoveEventListener(u
"mousedown"_ns
, this, false);
481 mOuter
->GetContent()->RemoveEventListener(u
"mousemove"_ns
, this, false);
482 mOuter
->GetContent()->RemoveEventListener(u
"mouseout"_ns
, this, false);
485 nsresult
nsSplitterFrameInner::HandleEvent(dom::Event
* aEvent
) {
486 nsAutoString eventType
;
487 aEvent
->GetType(eventType
);
488 if (eventType
.EqualsLiteral("mouseup")) return MouseUp(aEvent
);
489 if (eventType
.EqualsLiteral("mousedown")) return MouseDown(aEvent
);
490 if (eventType
.EqualsLiteral("mousemove") ||
491 eventType
.EqualsLiteral("mouseout"))
492 return MouseMove(aEvent
);
494 MOZ_ASSERT_UNREACHABLE("Unexpected eventType");
498 nsresult
nsSplitterFrameInner::MouseUp(Event
* aMouseEvent
) {
499 NS_ENSURE_TRUE(mOuter
, NS_OK
);
502 PresShell::ReleaseCapturingContent();
507 nsresult
nsSplitterFrameInner::MouseDown(Event
* aMouseEvent
) {
508 NS_ENSURE_TRUE(mOuter
, NS_OK
);
509 dom::MouseEvent
* mouseEvent
= aMouseEvent
->AsMouseEvent();
514 // only if left button
515 if (mouseEvent
->Button() != 0) return NS_OK
;
517 if (SplitterElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::disabled
,
518 nsGkAtoms::_true
, eCaseMatters
))
521 mParentBox
= nsIFrame::GetParentXULBox(mOuter
);
527 nsPresContext
* outerPresContext
= mOuter
->PresContext();
529 const int32_t childCount
= mParentBox
->PrincipalChildList().GetLength();
530 RefPtr
<gfxContext
> rc
=
531 outerPresContext
->PresShell()->CreateReferenceRenderingContext();
532 nsBoxLayoutState
state(outerPresContext
, rc
);
537 bool isHorizontal
= !mOuter
->IsXULHorizontal();
539 ResizeType resizeBefore
= GetResizeBefore();
540 ResizeType resizeAfter
= GetResizeAfter();
542 mChildInfosBefore
= MakeUnique
<nsSplitterInfo
[]>(childCount
);
543 mChildInfosAfter
= MakeUnique
<nsSplitterInfo
[]>(childCount
);
545 // create info 2 lists. One of the children before us and one after.
547 mChildInfosBeforeCount
= 0;
548 mChildInfosAfterCount
= 0;
550 bool foundOuter
= false;
551 CSSOrderAwareFrameIterator
iter(
552 mParentBox
, layout::kPrincipalList
,
553 CSSOrderAwareFrameIterator::ChildFilter::IncludeAll
,
554 CSSOrderAwareFrameIterator::OrderState::Unknown
,
555 CSSOrderAwareFrameIterator::OrderingProperty::BoxOrdinalGroup
);
556 for (; !iter
.AtEnd(); iter
.Next()) {
557 nsIFrame
* childBox
= iter
.get();
558 if (childBox
== mOuter
) {
561 // We're at the beginning, nothing to do.
564 if (count
== childCount
- 1 && resizeAfter
!= Grow
) {
565 // if it's the last index then we need to allow for resizeafter="grow"
571 nsIContent
* content
= childBox
->GetContent();
573 // skip over any splitters
574 if (content
->NodeInfo()->NameAtom() != nsGkAtoms::splitter
) {
575 nsSize prefSize
= childBox
->GetXULPrefSize(state
);
576 nsSize minSize
= childBox
->GetXULMinSize(state
);
577 nsSize maxSize
= nsIFrame::XULBoundsCheckMinMax(
578 minSize
, childBox
->GetXULMaxSize(state
));
579 prefSize
= nsIFrame::XULBoundsCheck(minSize
, prefSize
, maxSize
);
581 nsSplitterFrame::AddXULMargin(childBox
, minSize
);
582 nsSplitterFrame::AddXULMargin(childBox
, prefSize
);
583 nsSplitterFrame::AddXULMargin(childBox
, maxSize
);
585 nscoord flex
= childBox
->GetXULFlex();
587 nsMargin
margin(0, 0, 0, 0);
588 childBox
->GetXULMargin(margin
);
589 nsRect
r(childBox
->GetRect());
592 // We need to check for hidden attribute too, since treecols with
593 // the hidden="true" attribute are not really hidden, just collapsed
594 if (!content
->IsElement() || (!content
->AsElement()->AttrValueIs(
595 kNameSpaceID_None
, nsGkAtoms::fixed
,
596 nsGkAtoms::_true
, eCaseMatters
) &&
597 !content
->AsElement()->AttrValueIs(
598 kNameSpaceID_None
, nsGkAtoms::hidden
,
599 nsGkAtoms::_true
, eCaseMatters
))) {
600 if (!foundOuter
&& (resizeBefore
!= Flex
|| flex
> 0)) {
601 mChildInfosBefore
[mChildInfosBeforeCount
].childElem
= content
;
602 mChildInfosBefore
[mChildInfosBeforeCount
].min
=
603 isHorizontal
? minSize
.width
: minSize
.height
;
604 mChildInfosBefore
[mChildInfosBeforeCount
].max
=
605 isHorizontal
? maxSize
.width
: maxSize
.height
;
606 mChildInfosBefore
[mChildInfosBeforeCount
].current
=
607 isHorizontal
? r
.width
: r
.height
;
608 mChildInfosBefore
[mChildInfosBeforeCount
].flex
= flex
;
609 mChildInfosBefore
[mChildInfosBeforeCount
].changed
=
610 mChildInfosBefore
[mChildInfosBeforeCount
].current
;
611 mChildInfosBeforeCount
++;
612 } else if (foundOuter
&& (resizeAfter
!= Flex
|| flex
> 0)) {
613 mChildInfosAfter
[mChildInfosAfterCount
].childElem
= content
;
614 mChildInfosAfter
[mChildInfosAfterCount
].min
=
615 isHorizontal
? minSize
.width
: minSize
.height
;
616 mChildInfosAfter
[mChildInfosAfterCount
].max
=
617 isHorizontal
? maxSize
.width
: maxSize
.height
;
618 mChildInfosAfter
[mChildInfosAfterCount
].current
=
619 isHorizontal
? r
.width
: r
.height
;
620 mChildInfosAfter
[mChildInfosAfterCount
].flex
= flex
;
621 mChildInfosAfter
[mChildInfosAfterCount
].changed
=
622 mChildInfosAfter
[mChildInfosAfterCount
].current
;
623 mChildInfosAfterCount
++;
635 if (!mParentBox
->IsXULNormalDirection()) {
636 // The before array is really the after array, and the order needs to be
637 // reversed. First reverse both arrays.
638 Reverse(mChildInfosBefore
, mChildInfosBeforeCount
);
639 Reverse(mChildInfosAfter
, mChildInfosAfterCount
);
641 // Now swap the two arrays.
642 std::swap(mChildInfosBeforeCount
, mChildInfosAfterCount
);
643 std::swap(mChildInfosBefore
, mChildInfosAfter
);
646 // if resizebefore is not Farthest, reverse the list because the first child
647 // in the list is the farthest, and we want the first child to be the closest.
648 if (resizeBefore
!= Farthest
)
649 Reverse(mChildInfosBefore
, mChildInfosBeforeCount
);
651 // if the resizeafter is the Farthest we must reverse the list because the
652 // first child in the list is the closest we want the first child to be the
654 if (resizeAfter
== Farthest
) Reverse(mChildInfosAfter
, mChildInfosAfterCount
);
656 // grow only applys to the children after. If grow is set then no space should
657 // be taken out of any children after us. To do this we just set the size of
658 // that list to be 0.
659 if (resizeAfter
== Grow
) mChildInfosAfterCount
= 0;
663 nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(mouseEvent
, mParentBox
);
666 mSplitterPos
= mOuter
->mRect
.x
;
669 mSplitterPos
= mOuter
->mRect
.y
;
674 // printf("Pressed mDragStart=%d\n",mDragStart);
676 PresShell::SetCapturingContent(mOuter
->GetContent(),
677 CaptureFlags::IgnoreAllowedState
);
682 nsresult
nsSplitterFrameInner::MouseMove(Event
* aMouseEvent
) {
683 NS_ENSURE_TRUE(mOuter
, NS_OK
);
684 if (!mPressed
) return NS_OK
;
686 if (mDragging
) return NS_OK
;
688 nsCOMPtr
<nsIDOMEventListener
> kungfuDeathGrip(this);
689 mOuter
->mContent
->AsElement()->SetAttr(kNameSpaceID_None
, nsGkAtoms::state
,
690 u
"dragging"_ns
, true);
698 void nsSplitterFrameInner::Reverse(UniquePtr
<nsSplitterInfo
[]>& aChildInfos
,
700 UniquePtr
<nsSplitterInfo
[]> infos(new nsSplitterInfo
[aCount
]);
702 for (int i
= 0; i
< aCount
; i
++) infos
[i
] = aChildInfos
[aCount
- 1 - i
];
704 aChildInfos
= std::move(infos
);
707 bool nsSplitterFrameInner::SupportsCollapseDirection(
708 nsSplitterFrameInner::CollapseDirection aDirection
) {
709 static Element::AttrValuesArray strings
[] = {
710 nsGkAtoms::before
, nsGkAtoms::after
, nsGkAtoms::both
, nullptr};
712 switch (SplitterElement()->FindAttrValueIn(
713 kNameSpaceID_None
, nsGkAtoms::collapse
, strings
, eCaseMatters
)) {
715 return (aDirection
== Before
);
717 return (aDirection
== After
);
725 void nsSplitterFrameInner::UpdateState() {
726 // State Transitions:
728 // Open -> CollapsedBefore
729 // Open -> CollapsedAfter
730 // CollapsedBefore -> Open
731 // CollapsedBefore -> Dragging
732 // CollapsedAfter -> Open
733 // CollapsedAfter -> Dragging
735 // Dragging -> CollapsedBefore (auto collapse)
736 // Dragging -> CollapsedAfter (auto collapse)
738 State newState
= GetState();
740 if (newState
== mState
) {
745 if ((SupportsCollapseDirection(Before
) || SupportsCollapseDirection(After
)) &&
746 mOuter
->GetParent()->IsXULBoxFrame()) {
747 // Find the splitter's immediate sibling.
748 const bool prev
= newState
== CollapsedBefore
|| mState
== CollapsedBefore
;
749 nsIFrame
* splitterSibling
=
750 nsBoxFrame::SlowOrdinalGroupAwareSibling(mOuter
, !prev
);
751 if (splitterSibling
) {
752 nsCOMPtr
<nsIContent
> sibling
= splitterSibling
->GetContent();
753 if (sibling
&& sibling
->IsElement()) {
754 if (mState
== CollapsedBefore
|| mState
== CollapsedAfter
) {
755 // CollapsedBefore -> Open
756 // CollapsedBefore -> Dragging
757 // CollapsedAfter -> Open
758 // CollapsedAfter -> Dragging
759 nsContentUtils::AddScriptRunner(new nsUnsetAttrRunnable(
760 sibling
->AsElement(), nsGkAtoms::collapsed
));
761 } else if ((mState
== Open
|| mState
== Dragging
) &&
762 (newState
== CollapsedBefore
||
763 newState
== CollapsedAfter
)) {
764 // Open -> CollapsedBefore / CollapsedAfter
765 // Dragging -> CollapsedBefore / CollapsedAfter
766 nsContentUtils::AddScriptRunner(new nsSetAttrRunnable(
767 sibling
->AsElement(), nsGkAtoms::collapsed
, u
"true"_ns
));
775 void nsSplitterFrameInner::EnsureOrient() {
776 bool isHorizontal
= !mParentBox
->HasAnyStateBits(NS_STATE_IS_HORIZONTAL
);
778 mOuter
->AddStateBits(NS_STATE_IS_HORIZONTAL
);
780 mOuter
->RemoveStateBits(NS_STATE_IS_HORIZONTAL
);
783 void nsSplitterFrameInner::AdjustChildren(nsPresContext
* aPresContext
) {
785 bool isHorizontal
= !mOuter
->IsXULHorizontal();
787 AdjustChildren(aPresContext
, mChildInfosBefore
.get(), mChildInfosBeforeCount
,
789 AdjustChildren(aPresContext
, mChildInfosAfter
.get(), mChildInfosAfterCount
,
793 static nsIFrame
* GetChildBoxForContent(nsIFrame
* aParentBox
,
794 nsIContent
* aContent
) {
795 nsIFrame
* childBox
= nsIFrame::GetChildXULBox(aParentBox
);
798 if (childBox
->GetContent() == aContent
) {
801 childBox
= nsIFrame::GetNextXULBox(childBox
);
806 void nsSplitterFrameInner::AdjustChildren(nsPresContext
* aPresContext
,
807 nsSplitterInfo
* aChildInfos
,
808 int32_t aCount
, bool aIsHorizontal
) {
809 /// printf("------- AdjustChildren------\n");
811 nsBoxLayoutState
state(aPresContext
);
813 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
815 // first set all the widths.
816 nsIFrame
* child
= nsIFrame::GetChildXULBox(mOuter
);
818 SetPreferredSize(state
, child
, onePixel
, aIsHorizontal
, nullptr);
819 child
= nsIFrame::GetNextXULBox(child
);
822 // now set our changed widths.
823 for (int i
= 0; i
< aCount
; i
++) {
824 nscoord pref
= aChildInfos
[i
].changed
;
826 GetChildBoxForContent(mParentBox
, aChildInfos
[i
].childElem
);
829 SetPreferredSize(state
, childBox
, onePixel
, aIsHorizontal
, &pref
);
834 void nsSplitterFrameInner::SetPreferredSize(nsBoxLayoutState
& aState
,
839 nsRect
rect(aChildBox
->GetRect());
851 nsMargin
margin(0, 0, 0, 0);
852 aChildBox
->GetXULMargin(margin
);
854 RefPtr
<nsAtom
> attribute
;
857 pref
-= (margin
.left
+ margin
.right
);
858 attribute
= nsGkAtoms::width
;
860 pref
-= (margin
.top
+ margin
.bottom
);
861 attribute
= nsGkAtoms::height
;
864 nsIContent
* content
= aChildBox
->GetContent();
865 if (!content
->IsElement()) {
869 // set its preferred size.
870 nsAutoString prefValue
;
871 prefValue
.AppendInt(pref
/ aOnePixel
);
872 if (content
->AsElement()->AttrValueIs(kNameSpaceID_None
, attribute
, prefValue
,
877 AutoWeakFrame
weakBox(aChildBox
);
878 content
->AsElement()->SetAttr(kNameSpaceID_None
, attribute
, prefValue
, true);
879 NS_ENSURE_TRUE_VOID(weakBox
.IsAlive());
880 aState
.PresShell()->FrameNeedsReflow(aChildBox
, IntrinsicDirty::StyleChange
,
884 void nsSplitterFrameInner::AddRemoveSpace(nscoord aDiff
,
885 nsSplitterInfo
* aChildInfos
,
886 int32_t aCount
, int32_t& aSpaceLeft
) {
889 for (int i
= 0; i
< aCount
; i
++) {
890 nscoord min
= aChildInfos
[i
].min
;
891 nscoord max
= aChildInfos
[i
].max
;
892 nscoord
& c
= aChildInfos
[i
].changed
;
894 // figure our how much space to add or remove
895 if (c
+ aDiff
< min
) {
898 } else if (c
+ aDiff
> max
) {
906 // there is not space left? We are done
907 if (aDiff
== 0) break;
914 * Ok if we want to resize a child we will know the actual size in pixels we
915 * want it to be. This is not the preferred size. But they only way we can
916 * change a child is my manipulating its preferred size. So give the actual
917 * pixel size this return method will return figure out the preferred size and
921 void nsSplitterFrameInner::ResizeChildTo(nscoord
& aDiff
,
922 nsSplitterInfo
* aChildrenBeforeInfos
,
923 nsSplitterInfo
* aChildrenAfterInfos
,
924 int32_t aChildrenBeforeCount
,
925 int32_t aChildrenAfterCount
,
928 AddRemoveSpace(aDiff
, aChildrenBeforeInfos
, aChildrenBeforeCount
, spaceLeft
);
930 // if there is any space left over remove it from the dif we were originally
933 AddRemoveSpace(-aDiff
, aChildrenAfterInfos
, aChildrenAfterCount
, spaceLeft
);
935 if (spaceLeft
!= 0) {
938 AddRemoveSpace(spaceLeft
, aChildrenBeforeInfos
, aChildrenBeforeCount
,