Files
palemoon27/layout/xul/nsSplitterFrame.cpp
roytam1 4696208ad8 import changes from `dev' branch of rmottola/Arctic-Fox:- Bug 1258966 - Remove unnecessary null-checks of MediaDecoderStateMachine::mReader. r=bechen. (0513c82214)
- Bug 1258627 - always schedule next cycle so MDSM has a chance to leave buffering state. r=cpearce. (49fb876b9e)
- Bug 1258271 - Remove arguments from MediaDecoderStateMachine::HaveEnoughDecodedAudio(). r=bechen. (e8610f7f76)
- Bug 1252753. Part 1 - remove calls to PushFront(). r=kinetik. (7abe1bfb0f)
- Bug 1252753. Part 2 - remove MediaDecoderStateMachine::PushFront(). r=kinetik. (1a153a5b00)
- Bug 1230527: P1. Ensure seeked event is fired prior loadeddata. r=jwwang (aa9eef9fd3)
- Bug 1230527: P2. Add mochitest. r=jwwang (6e9f920781)
- Bug 1252767 - Remove MediaDecoderStateMachine::mPendingSeek. r=cpearce. (e5d69a191d)
- Bug 1255268 - Replace SeekJob::Steal() with move semantics. r=cpearce. (6294c01ba7)
- Bug 1253490 - fix the calculation of decodeTime. r=jya. (b8f3c8801a)
- Bug 1252343. Part 1 - make mReader const. r=bechen. (33a8b6a4ad)
- Bug 1252343. Part 2 - remove null checks for mReader which is const and never null. r=bechen. (5cd068aafc)
- Bug 1250054. Part 2 - employ MediaDecoderReaderWrapper for MDSM and remove code about adjusting start time. r=jya. (b5a954d8d8)
- Bug 1252341 - No need to reset mReader in ~MediaDecoderStateMachine() because the destructor will do that. r=bechen. (cd0639e366)
- Bug 1264784 - part 2 - remove unused virtual methods from nsIFrame; r=dholbert (5d52314a79)
- Bug 1264784 - part 3 - make nsIFrame::GetNearestWidget methods non-virtual; r=dholbert (67b0987de6)
- Bug 1053986 - Rename nsIFrame::IsBoxFrame to IsXULBoxFrame. r=dholbert (394613f49f)
- Bug 1256040 - Fix some nsGridContainerFrame.h/cpp compile errors in non-unified build. r=dholbert (b49241e3b1)
- Bug 1264607 - Treat track size <percentage> values as 'auto' when the grid container size is indefinite. r=dholbert (3895d4d922)
- Bug 1260614 - Cleanup grid item iterator Reset() calls. r=dholbert (e6760e1def)
- Bug 1256040 - Bustage fix. r=me (d67b9c0de5)
- Bug 1233191 part 1 - Implement sanity checks on the flex/grid container child frame list. Remove the anon grid item sanity checks that the frame constructor now does instead. r=dholbert (d9412bb043)
- Bug 1233191 part 2 - Remove anon flex item sanity checks that the frame constructor now does instead. r=dholbert (564184ea6d)
- Bug 1233191 part 3 - crashtest. (c6b6bb4fca)
- Bug 1187846 Stack layout doesn't honour min/max sizes for positioned elements r=Enn (8080e9db71)
- Bug 1053986 - Rename nsIFrame::GetMinSize to GetXULMinSize, and related methods. r=dholbert (fa1722982e)
- Bug 1000870 - Add some features in testing system. r=smaug (f7b4b8916f)
- Bug 1053986 - Rename nsIFrame::GetPrefSize to GetXULPrefSize, and related methods. r=dholbert (9f7e8de26e)
- Bug 1053986 - Rename nsIFrame::GetMaxSize to GetXULMaxSize, and related methods. r=dholbert (a632913886)
- Bug 1053986 - Rename nsIFrame::GetMinSizeForScrollArea to GetXULMinSizeForScrollArea. r=dholbert (ac520afe6f)
- Bug 1053986 - Rename nsIFrame::GetOrdinal to GetXULOrdinal. r=dholbert (e5f7342d03)
- Bug 1053986 - Rename nsIFrame::GetFlex to GetXULFlex. r=dholbert (aadf567a8c)
- Bug 1053986 - Rename nsIFrame::GetBoxAscent to GetXULBoxAscent. r=dholbert (4e729c60ea)
- Bug 1053986 - Rename nsFrame.cpp static method IsBoxWrapped to IsXULBoxWrapped. r=dholbert (009b251df0)
- Bug 1053986 - Rename nsIFrame::IsCollapsed to IsXULCollapsed, and related methods. r=dholbert (eec509e378)
- Bug 1168212 - Ensure popups have a minimum width of their preferred size r=Enn (657c8da6fa)
- Bug 1053986 - Rename nsIFrame::SetBounds to SetXULBounds. r=dholbert (304ff47c6c)
- Bug 1088637 - check we get the right transition event, r=Enn (b0da4a67f6)
- Bug 1053986 - Rename nsIFrame::Layout to XULLayout, and related methods with the same name. r=dholbert (66ef396b10)
- Bug 1053986 - Rename nsIFrame::GetBorderAndPadding to GetXULBorderAndPadding. r=dholbert (588f824000)
- Bug 1053986 - Rename nsIFrame::GetBorder to GetXULBorder. r=dholbert (f91ae3fc59)
- Bug 1053986 - Rename nsIFrame::GetPadding to GetXULPadding. r=dholbert (797e21e6af)
- Bug 1053986 - Rename nsIFrame::GetMargin to GetXULMargin. r=dholbert (05dc0235f2)
- Bug 1053986 - Rename nsIFrame::SetLayoutManager to SetXULLayoutManager. r=dholbert (0cb22c411d)
- Bug 1053986 - Rename nsIFrame::GetLayoutManager to GetXULLayoutManager. r=dholbert (6a03e1de2c)
- Bug 852754 - Part 1: Share the code for limiting scale factors to all image types. r=mstange (098a083d1a)
- Bug 852754 - Part 2: Share the implementation of GetContainer. r=mstange (063b7683dd)
- Bug 852754 - Part 3: Share the implementation of ConfigureLayer. r=mstange (ffd2d99802)
- Bug 1256999 - Pass the right context to new channels for image loads. r=bz r=seth (275cebb231)
- Bug 1194337 - Context menu positioned incorrectly on OSX. r=enn (1e429a9d3c)
- Bug 1216284 - Tooltips do not flip correctly on OSX. r=enndeakin (c08931768b)
- Bug 1053986 - Rename nsIFrame::GetClientRect to GetXULClientRect. r=dholbert (c06a121de6)
- Bug 1053986 - Rename nsIFrame::GetVAlign to GetXULVAlign. r=dholbert (a923d76c4b)
- Bug 1053986 - Rename nsBox::GetChildBox to GetChildXULBox. r=dholbert (ebe12c77f1)
- Bug 1053986 - Rename nsIFrame::GetHAlign to GetXULHAlign. r=dholbert (dbd501e2bd)
- Bug 1053986 - Rename nsBox::GetNextBox to GetNextXULBox. r=dholbert (1cd5fa1ce3)
- Bug 1053986 - Rename nsBox::GetParentBox to GetParentXULBox. r=dholbert (2beaeb6bdd)
- Bug 1171696 - Don't resize scrollbar thumb when updating its position. r=mstange (5b51241744)
- Bug 1053986 - Rename nsIFrame::IsHorizontal to IsXULHorizontal, and related methods. r=dholbert (7d8e4142e5)
- Bug 1053986 - Rename nsIFrame::IsNormalDirection to IsXULNormalDirection. r=dholbert (7d9686b089)
- Bug 1053986 - Rename nsIFrame::Redraw to XULRedraw. r=dholbert (20da19c2ce)
- Bug 1053986 - Rename nsIFrame::RelayoutChildAtOrdinal to XULRelayoutChildAtOrdinal. r=dholbert (e5c4eb2b9f)
- Bug 1053986 - Rename nsIFrame::SetDebug to SetXULDebug. r=dholbert (438c3a1109)
- Bug 1053986 - Rename nsIFrame::GetDebug to GetXULDebug. r=dholbert (00e0ca19e4)
- Bug 1053986 - Rename nsIFrame::DumpBox to XULDumpBox. r=dholbert (30edc21d8e)
- Bug 1053986 - Rename nsIFrame::AddCSSPrefSize, AddCSSMinSize, AddCSSMaxSize, and AddCSSFlex by replacing CSS with XUL. r=dholbert (4e79b90b1f)
- Bug 1053986 - Fix ordering of methods in nsIFrame.h r=dholbert (fb08aa035f)
- Bug 1053986 - Rename nsBox::BeginLayout to BeginXULLayout. r=dholbert (595cb70526)
- Bug 1053986 - Rename nsBox::EndLayout to EndXULLayout. r=dholbert (13f98a84a0)
- Bug 1213895: Part 1 - Correctly support crop="none" in XUL labels. r=neil (053e0c8414)
- Bug 1053986 - Rename nsBox::DoLayout to DoXULLayout. r=dholbert (40ef0ece53)
- Bug 1144619 - Variable 'nextX' is created in the wrong scope. r=dbaron (7893ccb301)
- Bug 1172011 - Remove unneeded 'spaceLeft' declaration from nsSplitterFrame.cpp. r=froydnj (7ef937e283)
- Bug 1192376: Make nsImageBoxFrame check whether image size is available before trying to paint an image. r=seth (2db203eb3b)
- Bug 1240533 - Parameters to ScreenForRect need to be passed as desktop pixels, not device pixels. r=emk (d49b532344)
- Bug 1212658 - Remove needless IsCallerChrome check in nsMenuPopupFrame. r=bz (bdf0d16a49)
- Bug 374471 Make the noautohide attribute live where supported r=enndeakin (f696b4d174)
- Bug 1200870, allow -1 as a value to popup.moveTo, r=tn (10e05d4240)
- Bug 1182856 - Part 1: Add StopTransitionsForElement. r=heycam (1c0ac374c4)
- Bug 1182856 - Part 2: Let AnimationsWithDestroyFrame destroy transitions. r=heycam (7820f7b1e5)
- Bug 1182856 - Part 3: Cancel transitions for destroy frames. r=heycam (439fb07545)
- Bug 1157936 - Put the correct ratio on the scrollbar layer. r=tn (a64f35b9ea)
- Bug 1238137 - Telemetry pings for main thread scrollbar-driven scroll input methods. r=kats (89e5187b88)
- Bug 1156106 - Make nsMenuBarFrame::mMenuBarListener an nsRefPtr; r=roc (838126cd16)
- Bug 1163304 Close all existing popups when menubar becomes active because it should have pseudo focus and other popups shouldn't handle key events r=enn+neil (933c9ae40e)
- Bug 1252693 - Assert that we do not tenure into an OMT Zone; r=sfink (0812fc81f2)
- Bug 1265679 - Always call the object moved hook in generational GC r=terrence (6d75efba2d)
- Bug 1265825 - Remove mSuppressionActive assert. r=kats (3b6b3ba030)
- Bug 1250226 - Only report compacting GC telemetry for compacting GCs r=terrence (4e0f511ccf)
- Bug 1258578: Improve documentation for js::RelocatablePtr. DONTBUILD r=terrence (3ed2a933e0)
- Bug 1252713 - Fix FILES_PER_UNIFIED_FILE=1 bustage in js/. r=terrence (8a97e6c7e2)
- Bug 1265741: Fix unified build for fuzzers; r=nbp (ad3b4a4543)
- Bug 1013219 - set the line number of the terminating retrval; r=jimb, r=ejpbruel, r=fitzgen (df810884a9)
- Bug 1013219 - set line number of return instruction; r=efaust, r=fitzgen, r=ejpbruel (503b1a2bf3)
- Bug 1260577 - Fix |obj[expr] += e2| erroneously calling expr.toString() twice. (r=till) (e2485baccb)
- Bug 1260577 - followup: Change confusing name SelfAssign to CompoundAssign. (rs=jorendorff) (51a7dee68e)
- Bug 1263881 - Check the the number of body level lexicals doesn't exceed that which we can store in Bindings r=shu (d610d7a1e9)
- Bug 1258097 - Check for redeclaration of imports by functions r=shu (78f06f273f)
- Bug 1264954 - Add missing OOM check in Parser::templateLiteral. r=jonco (2743a82e1e)
- Bug 1265313 - Fix Annex B.3.5 handling with body-level lexicals. (r=jorendorff) (6b4139d25b)
- Bug 1260620 - Ensure that possibleErrors are not null before attempting to check them; r=jorendorff (6b284c1107)
- Bug 1253275 - Remove const_casts from Runtime.cpp. r=sfink (40b6d68455)
- Bug 1262731 - Add JS_InitWithFailureDiagnostic(). r=sfink. (310579fcd3)
- Bug 1263886 - Don't call makeConstructorCode if the group has unknown properties. r=bhackett (e81c939928)
- Bug 1260891 - Acquire and release the lock when destorying an `ExclusiveData<T>`'s protected value; r=terrence a=kwierso (dc39af575e)
- Bug 1252034 - Value Numbering: Unconditionally generate fixup blocks. r=sunfish (3b46b2df51)
- Bug 1232229 - Ensure generator object prototype is a singleton and tenured. r=jonco (d0c4f17cb6)
2024-04-25 21:58:07 +08:00

1048 lines
31 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//
// Eric Vaughan
// Netscape Communications
//
// See documentation in associated header file
//
#include "nsSplitterFrame.h"
#include "nsGkAtoms.h"
#include "nsIDOMElement.h"
#include "nsIDOMXULElement.h"
#include "nsPresContext.h"
#include "nsRenderingContext.h"
#include "nsIDocument.h"
#include "nsNameSpaceManager.h"
#include "nsScrollbarButtonFrame.h"
#include "nsIDOMEventListener.h"
#include "nsIDOMMouseEvent.h"
#include "nsIPresShell.h"
#include "nsFrameList.h"
#include "nsHTMLParts.h"
#include "nsStyleContext.h"
#include "nsBoxLayoutState.h"
#include "nsIServiceManager.h"
#include "nsContainerFrame.h"
#include "nsAutoPtr.h"
#include "nsContentCID.h"
#include "mozilla/StyleSetHandle.h"
#include "mozilla/StyleSetHandleInlines.h"
#include "nsLayoutUtils.h"
#include "nsDisplayList.h"
#include "nsContentUtils.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/UniquePtr.h"
using namespace mozilla;
class nsSplitterInfo {
public:
nscoord min;
nscoord max;
nscoord current;
nscoord changed;
nsCOMPtr<nsIContent> childElem;
int32_t flex;
int32_t index;
};
class nsSplitterFrameInner final : public nsIDOMEventListener
{
protected:
virtual ~nsSplitterFrameInner();
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
explicit nsSplitterFrameInner(nsSplitterFrame* aSplitter)
{
mOuter = aSplitter;
mPressed = false;
}
void Disconnect() { mOuter = nullptr; }
nsresult MouseDown(nsIDOMEvent* aMouseEvent);
nsresult MouseUp(nsIDOMEvent* aMouseEvent);
nsresult MouseMove(nsIDOMEvent* aMouseEvent);
void MouseDrag(nsPresContext* aPresContext, WidgetGUIEvent* aEvent);
void MouseUp(nsPresContext* aPresContext, WidgetGUIEvent* aEvent);
void AdjustChildren(nsPresContext* aPresContext);
void AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal);
void AddRemoveSpace(nscoord aDiff,
nsSplitterInfo* aChildInfos,
int32_t aCount,
int32_t& aSpaceLeft);
void ResizeChildTo(nscoord& aDiff,
nsSplitterInfo* aChildrenBeforeInfos,
nsSplitterInfo* aChildrenAfterInfos,
int32_t aChildrenBeforeCount,
int32_t aChildrenAfterCount,
bool aBounded);
void UpdateState();
void AddListener();
void RemoveListener();
enum ResizeType { Closest, Farthest, Flex, Grow };
enum State { Open, CollapsedBefore, CollapsedAfter, Dragging };
enum CollapseDirection { Before, After };
ResizeType GetResizeBefore();
ResizeType GetResizeAfter();
State GetState();
void Reverse(UniquePtr<nsSplitterInfo[]>& aIndexes, int32_t aCount);
bool SupportsCollapseDirection(CollapseDirection aDirection);
void EnsureOrient();
void SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize);
nsSplitterFrame* mOuter;
bool mDidDrag;
nscoord mDragStart;
nscoord mCurrentPos;
nsIFrame* mParentBox;
bool mPressed;
UniquePtr<nsSplitterInfo[]> mChildInfosBefore;
UniquePtr<nsSplitterInfo[]> mChildInfosAfter;
int32_t mChildInfosBeforeCount;
int32_t mChildInfosAfterCount;
State mState;
nscoord mSplitterPos;
bool mDragging;
};
NS_IMPL_ISUPPORTS(nsSplitterFrameInner, nsIDOMEventListener)
nsSplitterFrameInner::ResizeType
nsSplitterFrameInner::GetResizeBefore()
{
static nsIContent::AttrValuesArray strings[] =
{&nsGkAtoms::farthest, &nsGkAtoms::flex, nullptr};
switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None,
nsGkAtoms::resizebefore,
strings, eCaseMatters)) {
case 0: return Farthest;
case 1: return Flex;
}
return Closest;
}
nsSplitterFrameInner::~nsSplitterFrameInner()
{
}
nsSplitterFrameInner::ResizeType
nsSplitterFrameInner::GetResizeAfter()
{
static nsIContent::AttrValuesArray strings[] =
{&nsGkAtoms::farthest, &nsGkAtoms::flex, &nsGkAtoms::grow, nullptr};
switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None,
nsGkAtoms::resizeafter,
strings, eCaseMatters)) {
case 0: return Farthest;
case 1: return Flex;
case 2: return Grow;
}
return Closest;
}
nsSplitterFrameInner::State
nsSplitterFrameInner::GetState()
{
static nsIContent::AttrValuesArray strings[] =
{&nsGkAtoms::dragging, &nsGkAtoms::collapsed, nullptr};
static nsIContent::AttrValuesArray strings_substate[] =
{&nsGkAtoms::before, &nsGkAtoms::after, nullptr};
switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None,
nsGkAtoms::state,
strings, eCaseMatters)) {
case 0: return Dragging;
case 1:
switch (mOuter->GetContent()->FindAttrValueIn(kNameSpaceID_None,
nsGkAtoms::substate,
strings_substate,
eCaseMatters)) {
case 0: return CollapsedBefore;
case 1: return CollapsedAfter;
default:
if (SupportsCollapseDirection(After))
return CollapsedAfter;
return CollapsedBefore;
}
}
return Open;
}
//
// NS_NewSplitterFrame
//
// Creates a new Toolbar frame and returns it
//
nsIFrame*
NS_NewSplitterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsSplitterFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsSplitterFrame)
nsSplitterFrame::nsSplitterFrame(nsStyleContext* aContext)
: nsBoxFrame(aContext),
mInner(0)
{
}
void
nsSplitterFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
if (mInner) {
mInner->RemoveListener();
mInner->Disconnect();
mInner->Release();
mInner = nullptr;
}
nsBoxFrame::DestroyFrom(aDestructRoot);
}
nsresult
nsSplitterFrame::GetCursor(const nsPoint& aPoint,
nsIFrame::Cursor& aCursor)
{
return nsBoxFrame::GetCursor(aPoint, aCursor);
/*
if (IsXULHorizontal())
aCursor = NS_STYLE_CURSOR_N_RESIZE;
else
aCursor = NS_STYLE_CURSOR_W_RESIZE;
return NS_OK;
*/
}
nsresult
nsSplitterFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType)
{
nsresult rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute,
aModType);
// if the alignment changed. Let the grippy know
if (aAttribute == nsGkAtoms::align) {
// tell the slider its attribute changed so it can
// update itself
nsIFrame* grippy = nullptr;
nsScrollbarButtonFrame::GetChildWithTag(nsGkAtoms::grippy, this, grippy);
if (grippy)
grippy->AttributeChanged(aNameSpaceID, aAttribute, aModType);
} else if (aAttribute == nsGkAtoms::state) {
mInner->UpdateState();
}
return rv;
}
/**
* Initialize us. If we are in a box get our alignment so we know what direction we are
*/
void
nsSplitterFrame::Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow)
{
MOZ_ASSERT(!mInner);
mInner = new nsSplitterFrameInner(this);
mInner->AddRef();
mInner->mState = nsSplitterFrameInner::Open;
mInner->mDragging = false;
// determine orientation of parent, and if vertical, set orient to vertical
// on splitter content, then re-resolve style
// XXXbz this is pretty messed up, since this can change whether we should
// have a frame at all. This really needs a better solution.
if (aParent && aParent->IsXULBoxFrame()) {
if (!aParent->IsXULHorizontal()) {
if (!nsContentUtils::HasNonEmptyAttr(aContent, kNameSpaceID_None,
nsGkAtoms::orient)) {
aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
NS_LITERAL_STRING("vertical"), false);
nsStyleContext* parentStyleContext = StyleContext()->GetParent();
RefPtr<nsStyleContext> newContext = PresContext()->StyleSet()->
ResolveStyleFor(aContent->AsElement(), parentStyleContext);
SetStyleContextWithoutNotification(newContext);
}
}
}
nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
mInner->mState = nsSplitterFrameInner::Open;
mInner->AddListener();
mInner->mParentBox = nullptr;
}
NS_IMETHODIMP
nsSplitterFrame::DoXULLayout(nsBoxLayoutState& aState)
{
if (GetStateBits() & NS_FRAME_FIRST_REFLOW)
{
mInner->mParentBox = nsBox::GetParentXULBox(this);
mInner->UpdateState();
}
return nsBoxFrame::DoXULLayout(aState);
}
void
nsSplitterFrame::GetInitialOrientation(bool& aIsHorizontal)
{
nsIFrame* box = nsBox::GetParentXULBox(this);
if (box) {
aIsHorizontal = !box->IsXULHorizontal();
}
else
nsBoxFrame::GetInitialOrientation(aIsHorizontal);
}
NS_IMETHODIMP
nsSplitterFrame::HandlePress(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
return NS_OK;
}
NS_IMETHODIMP
nsSplitterFrame::HandleMultiplePress(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus,
bool aControlHeld)
{
return NS_OK;
}
NS_IMETHODIMP
nsSplitterFrame::HandleDrag(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
return NS_OK;
}
NS_IMETHODIMP
nsSplitterFrame::HandleRelease(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
return NS_OK;
}
void
nsSplitterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
nsBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
// if the mouse is captured always return us as the frame.
if (mInner->mDragging)
{
// XXX It's probably better not to check visibility here, right?
aLists.Outlines()->AppendNewToTop(new (aBuilder)
nsDisplayEventReceiver(aBuilder, this));
return;
}
}
nsresult
nsSplitterFrame::HandleEvent(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent,
nsEventStatus* aEventStatus)
{
NS_ENSURE_ARG_POINTER(aEventStatus);
if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
return NS_OK;
}
nsWeakFrame weakFrame(this);
RefPtr<nsSplitterFrameInner> kungFuDeathGrip(mInner);
switch (aEvent->mMessage) {
case eMouseMove:
mInner->MouseDrag(aPresContext, aEvent);
break;
case eMouseUp:
if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
mInner->MouseUp(aPresContext, aEvent);
}
break;
default:
break;
}
NS_ENSURE_STATE(weakFrame.IsAlive());
return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
}
void
nsSplitterFrameInner::MouseUp(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent)
{
if (mDragging && mOuter) {
AdjustChildren(aPresContext);
AddListener();
nsIPresShell::SetCapturingContent(nullptr, 0); // XXXndeakin is this needed?
mDragging = false;
State newState = GetState();
// if the state is dragging then make it Open.
if (newState == Dragging)
mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state, EmptyString(), true);
mPressed = false;
// if we dragged then fire a command event.
if (mDidDrag) {
nsCOMPtr<nsIDOMXULElement> element = do_QueryInterface(mOuter->GetContent());
element->DoCommand();
}
//printf("MouseUp\n");
}
mChildInfosBefore = nullptr;
mChildInfosAfter = nullptr;
mChildInfosBeforeCount = 0;
mChildInfosAfterCount = 0;
}
void
nsSplitterFrameInner::MouseDrag(nsPresContext* aPresContext,
WidgetGUIEvent* aEvent)
{
if (mDragging && mOuter) {
//printf("Dragging\n");
bool isHorizontal = !mOuter->IsXULHorizontal();
// convert coord to pixels
nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent,
mParentBox);
nscoord pos = isHorizontal ? pt.x : pt.y;
// mDragStart is in frame coordinates
nscoord start = mDragStart;
// take our current position and subtract the start location
pos -= start;
//printf("Diff=%d\n", pos);
ResizeType resizeAfter = GetResizeAfter();
bool bounded;
if (resizeAfter == nsSplitterFrameInner::Grow)
bounded = false;
else
bounded = true;
int i;
for (i=0; i < mChildInfosBeforeCount; i++)
mChildInfosBefore[i].changed = mChildInfosBefore[i].current;
for (i=0; i < mChildInfosAfterCount; i++)
mChildInfosAfter[i].changed = mChildInfosAfter[i].current;
nscoord oldPos = pos;
ResizeChildTo(pos,
mChildInfosBefore.get(), mChildInfosAfter.get(),
mChildInfosBeforeCount, mChildInfosAfterCount, bounded);
State currentState = GetState();
bool supportsBefore = SupportsCollapseDirection(Before);
bool supportsAfter = SupportsCollapseDirection(After);
const bool isRTL = mOuter->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
bool pastEnd = oldPos > 0 && oldPos > pos;
bool pastBegin = oldPos < 0 && oldPos < pos;
if (isRTL) {
// Swap the boundary checks in RTL mode
bool tmp = pastEnd;
pastEnd = pastBegin;
pastBegin = tmp;
}
const bool isCollapsedBefore = pastBegin && supportsBefore;
const bool isCollapsedAfter = pastEnd && supportsAfter;
// if we are in a collapsed position
if (isCollapsedBefore || isCollapsedAfter)
{
// and we are not collapsed then collapse
if (currentState == Dragging) {
if (pastEnd)
{
//printf("Collapse right\n");
if (supportsAfter)
{
nsCOMPtr<nsIContent> outer = mOuter->mContent;
outer->SetAttr(kNameSpaceID_None, nsGkAtoms::substate,
NS_LITERAL_STRING("after"),
true);
outer->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
NS_LITERAL_STRING("collapsed"),
true);
}
} else if (pastBegin)
{
//printf("Collapse left\n");
if (supportsBefore)
{
nsCOMPtr<nsIContent> outer = mOuter->mContent;
outer->SetAttr(kNameSpaceID_None, nsGkAtoms::substate,
NS_LITERAL_STRING("before"),
true);
outer->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
NS_LITERAL_STRING("collapsed"),
true);
}
}
}
} else {
// if we are not in a collapsed position and we are not dragging make sure
// we are dragging.
if (currentState != Dragging)
mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state, NS_LITERAL_STRING("dragging"), true);
AdjustChildren(aPresContext);
}
mDidDrag = true;
}
}
void
nsSplitterFrameInner::AddListener()
{
mOuter->GetContent()->
AddEventListener(NS_LITERAL_STRING("mouseup"), this, false, false);
mOuter->GetContent()->
AddEventListener(NS_LITERAL_STRING("mousedown"), this, false, false);
mOuter->GetContent()->
AddEventListener(NS_LITERAL_STRING("mousemove"), this, false, false);
mOuter->GetContent()->
AddEventListener(NS_LITERAL_STRING("mouseout"), this, false, false);
}
void
nsSplitterFrameInner::RemoveListener()
{
ENSURE_TRUE(mOuter);
mOuter->GetContent()->
RemoveEventListener(NS_LITERAL_STRING("mouseup"), this, false);
mOuter->GetContent()->
RemoveEventListener(NS_LITERAL_STRING("mousedown"), this, false);
mOuter->GetContent()->
RemoveEventListener(NS_LITERAL_STRING("mousemove"), this, false);
mOuter->GetContent()->
RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, false);
}
nsresult
nsSplitterFrameInner::HandleEvent(nsIDOMEvent* aEvent)
{
nsAutoString eventType;
aEvent->GetType(eventType);
if (eventType.EqualsLiteral("mouseup"))
return MouseUp(aEvent);
if (eventType.EqualsLiteral("mousedown"))
return MouseDown(aEvent);
if (eventType.EqualsLiteral("mousemove") ||
eventType.EqualsLiteral("mouseout"))
return MouseMove(aEvent);
NS_ABORT();
return NS_OK;
}
nsresult
nsSplitterFrameInner::MouseUp(nsIDOMEvent* aMouseEvent)
{
NS_ENSURE_TRUE(mOuter, NS_OK);
mPressed = false;
nsIPresShell::SetCapturingContent(nullptr, 0);
return NS_OK;
}
nsresult
nsSplitterFrameInner::MouseDown(nsIDOMEvent* aMouseEvent)
{
NS_ENSURE_TRUE(mOuter, NS_OK);
nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aMouseEvent));
if (!mouseEvent)
return NS_OK;
int16_t button = 0;
mouseEvent->GetButton(&button);
// only if left button
if (button != 0)
return NS_OK;
if (mOuter->GetContent()->
AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
nsGkAtoms::_true, eCaseMatters))
return NS_OK;
mParentBox = nsBox::GetParentXULBox(mOuter);
if (!mParentBox)
return NS_OK;
// get our index
nsPresContext* outerPresContext = mOuter->PresContext();
const nsFrameList& siblingList(mParentBox->PrincipalChildList());
int32_t childIndex = siblingList.IndexOf(mOuter);
// if it's 0 (or not found) then stop right here.
// It might be not found if we're not in the parent's primary frame list.
if (childIndex <= 0)
return NS_OK;
int32_t childCount = siblingList.GetLength();
// if it's the last index then we need to allow for resizeafter="grow"
if (childIndex == childCount - 1 && GetResizeAfter() != Grow)
return NS_OK;
nsRenderingContext rc(
outerPresContext->PresShell()->CreateReferenceRenderingContext());
nsBoxLayoutState state(outerPresContext, &rc);
mCurrentPos = 0;
mPressed = true;
mDidDrag = false;
EnsureOrient();
bool isHorizontal = !mOuter->IsXULHorizontal();
ResizeType resizeBefore = GetResizeBefore();
ResizeType resizeAfter = GetResizeAfter();
mChildInfosBefore = MakeUnique<nsSplitterInfo[]>(childCount);
mChildInfosAfter = MakeUnique<nsSplitterInfo[]>(childCount);
// create info 2 lists. One of the children before us and one after.
int32_t count = 0;
mChildInfosBeforeCount = 0;
mChildInfosAfterCount = 0;
nsIFrame* childBox = nsBox::GetChildXULBox(mParentBox);
while (nullptr != childBox)
{
nsIContent* content = childBox->GetContent();
nsIDocument* doc = content->OwnerDoc();
int32_t dummy;
nsIAtom* atom = doc->BindingManager()->ResolveTag(content, &dummy);
// skip over any splitters
if (atom != nsGkAtoms::splitter) {
nsSize prefSize = childBox->GetXULPrefSize(state);
nsSize minSize = childBox->GetXULMinSize(state);
nsSize maxSize = nsBox::BoundsCheckMinMax(minSize, childBox->GetXULMaxSize(state));
prefSize = nsBox::BoundsCheck(minSize, prefSize, maxSize);
mOuter->AddMargin(childBox, minSize);
mOuter->AddMargin(childBox, prefSize);
mOuter->AddMargin(childBox, maxSize);
nscoord flex = childBox->GetXULFlex();
nsMargin margin(0,0,0,0);
childBox->GetXULMargin(margin);
nsRect r(childBox->GetRect());
r.Inflate(margin);
// We need to check for hidden attribute too, since treecols with
// the hidden="true" attribute are not really hidden, just collapsed
if (!content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::fixed,
nsGkAtoms::_true, eCaseMatters) &&
!content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
nsGkAtoms::_true, eCaseMatters)) {
if (count < childIndex && (resizeBefore != Flex || flex > 0)) {
mChildInfosBefore[mChildInfosBeforeCount].childElem = content;
mChildInfosBefore[mChildInfosBeforeCount].min = isHorizontal ? minSize.width : minSize.height;
mChildInfosBefore[mChildInfosBeforeCount].max = isHorizontal ? maxSize.width : maxSize.height;
mChildInfosBefore[mChildInfosBeforeCount].current = isHorizontal ? r.width : r.height;
mChildInfosBefore[mChildInfosBeforeCount].flex = flex;
mChildInfosBefore[mChildInfosBeforeCount].index = count;
mChildInfosBefore[mChildInfosBeforeCount].changed = mChildInfosBefore[mChildInfosBeforeCount].current;
mChildInfosBeforeCount++;
} else if (count > childIndex && (resizeAfter != Flex || flex > 0)) {
mChildInfosAfter[mChildInfosAfterCount].childElem = content;
mChildInfosAfter[mChildInfosAfterCount].min = isHorizontal ? minSize.width : minSize.height;
mChildInfosAfter[mChildInfosAfterCount].max = isHorizontal ? maxSize.width : maxSize.height;
mChildInfosAfter[mChildInfosAfterCount].current = isHorizontal ? r.width : r.height;
mChildInfosAfter[mChildInfosAfterCount].flex = flex;
mChildInfosAfter[mChildInfosAfterCount].index = count;
mChildInfosAfter[mChildInfosAfterCount].changed = mChildInfosAfter[mChildInfosAfterCount].current;
mChildInfosAfterCount++;
}
}
}
childBox = nsBox::GetNextXULBox(childBox);
count++;
}
if (!mParentBox->IsXULNormalDirection()) {
// The before array is really the after array, and the order needs to be reversed.
// First reverse both arrays.
Reverse(mChildInfosBefore, mChildInfosBeforeCount);
Reverse(mChildInfosAfter, mChildInfosAfterCount);
// Now swap the two arrays.
Swap(mChildInfosBeforeCount, mChildInfosAfterCount);
Swap(mChildInfosBefore, mChildInfosAfter);
}
// if resizebefore is not Farthest, reverse the list because the first child
// in the list is the farthest, and we want the first child to be the closest.
if (resizeBefore != Farthest)
Reverse(mChildInfosBefore, mChildInfosBeforeCount);
// if the resizeafter is the Farthest we must reverse the list because the first child in the list
// is the closest we want the first child to be the Farthest.
if (resizeAfter == Farthest)
Reverse(mChildInfosAfter, mChildInfosAfterCount);
// grow only applys to the children after. If grow is set then no space should be taken out of any children after
// us. To do this we just set the size of that list to be 0.
if (resizeAfter == Grow)
mChildInfosAfterCount = 0;
int32_t c;
nsPoint pt = nsLayoutUtils::GetDOMEventCoordinatesRelativeTo(mouseEvent->AsEvent(),
mParentBox);
if (isHorizontal) {
c = pt.x;
mSplitterPos = mOuter->mRect.x;
} else {
c = pt.y;
mSplitterPos = mOuter->mRect.y;
}
mDragStart = c;
//printf("Pressed mDragStart=%d\n",mDragStart);
nsIPresShell::SetCapturingContent(mOuter->GetContent(), CAPTURE_IGNOREALLOWED);
return NS_OK;
}
nsresult
nsSplitterFrameInner::MouseMove(nsIDOMEvent* aMouseEvent)
{
NS_ENSURE_TRUE(mOuter, NS_OK);
if (!mPressed)
return NS_OK;
if (mDragging)
return NS_OK;
nsCOMPtr<nsIDOMEventListener> kungfuDeathGrip(this);
mOuter->mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::state,
NS_LITERAL_STRING("dragging"), true);
RemoveListener();
mDragging = true;
return NS_OK;
}
void
nsSplitterFrameInner::Reverse(UniquePtr<nsSplitterInfo[]>& aChildInfos, int32_t aCount)
{
UniquePtr<nsSplitterInfo[]> infos(new nsSplitterInfo[aCount]);
for (int i=0; i < aCount; i++)
infos[i] = aChildInfos[aCount - 1 - i];
aChildInfos = Move(infos);
}
bool
nsSplitterFrameInner::SupportsCollapseDirection
(
nsSplitterFrameInner::CollapseDirection aDirection
)
{
static nsIContent::AttrValuesArray strings[] =
{&nsGkAtoms::before, &nsGkAtoms::after, &nsGkAtoms::both, nullptr};
switch (mOuter->mContent->FindAttrValueIn(kNameSpaceID_None,
nsGkAtoms::collapse,
strings, eCaseMatters)) {
case 0:
return (aDirection == Before);
case 1:
return (aDirection == After);
case 2:
return true;
}
return false;
}
void
nsSplitterFrameInner::UpdateState()
{
// State Transitions:
// Open -> Dragging
// Open -> CollapsedBefore
// Open -> CollapsedAfter
// CollapsedBefore -> Open
// CollapsedBefore -> Dragging
// CollapsedAfter -> Open
// CollapsedAfter -> Dragging
// Dragging -> Open
// Dragging -> CollapsedBefore (auto collapse)
// Dragging -> CollapsedAfter (auto collapse)
State newState = GetState();
if (newState == mState) {
// No change.
return;
}
if ((SupportsCollapseDirection(Before) || SupportsCollapseDirection(After)) &&
mOuter->GetParent()->IsXULBoxFrame()) {
// Find the splitter's immediate sibling.
nsIFrame* splitterSibling;
if (newState == CollapsedBefore || mState == CollapsedBefore) {
splitterSibling = mOuter->GetPrevSibling();
} else {
splitterSibling = mOuter->GetNextSibling();
}
if (splitterSibling) {
nsCOMPtr<nsIContent> sibling = splitterSibling->GetContent();
if (sibling) {
if (mState == CollapsedBefore || mState == CollapsedAfter) {
// CollapsedBefore -> Open
// CollapsedBefore -> Dragging
// CollapsedAfter -> Open
// CollapsedAfter -> Dragging
nsContentUtils::AddScriptRunner(
new nsUnsetAttrRunnable(sibling, nsGkAtoms::collapsed));
} else if ((mState == Open || mState == Dragging)
&& (newState == CollapsedBefore ||
newState == CollapsedAfter)) {
// Open -> CollapsedBefore / CollapsedAfter
// Dragging -> CollapsedBefore / CollapsedAfter
nsContentUtils::AddScriptRunner(
new nsSetAttrRunnable(sibling, nsGkAtoms::collapsed,
NS_LITERAL_STRING("true")));
}
}
}
}
mState = newState;
}
void
nsSplitterFrameInner::EnsureOrient()
{
bool isHorizontal = !(mParentBox->GetStateBits() & NS_STATE_IS_HORIZONTAL);
if (isHorizontal)
mOuter->mState |= NS_STATE_IS_HORIZONTAL;
else
mOuter->mState &= ~NS_STATE_IS_HORIZONTAL;
}
void
nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext)
{
EnsureOrient();
bool isHorizontal = !mOuter->IsXULHorizontal();
AdjustChildren(aPresContext, mChildInfosBefore.get(),
mChildInfosBeforeCount, isHorizontal);
AdjustChildren(aPresContext, mChildInfosAfter.get(),
mChildInfosAfterCount, isHorizontal);
}
static nsIFrame* GetChildBoxForContent(nsIFrame* aParentBox, nsIContent* aContent)
{
nsIFrame* childBox = nsBox::GetChildXULBox(aParentBox);
while (nullptr != childBox) {
if (childBox->GetContent() == aContent) {
return childBox;
}
childBox = nsBox::GetNextXULBox(childBox);
}
return nullptr;
}
void
nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext, nsSplitterInfo* aChildInfos, int32_t aCount, bool aIsHorizontal)
{
///printf("------- AdjustChildren------\n");
nsBoxLayoutState state(aPresContext);
nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
// first set all the widths.
nsIFrame* child = nsBox::GetChildXULBox(mOuter);
while(child)
{
SetPreferredSize(state, child, onePixel, aIsHorizontal, nullptr);
child = nsBox::GetNextXULBox(child);
}
// now set our changed widths.
for (int i=0; i < aCount; i++)
{
nscoord pref = aChildInfos[i].changed;
nsIFrame* childBox = GetChildBoxForContent(mParentBox, aChildInfos[i].childElem);
if (childBox) {
SetPreferredSize(state, childBox, onePixel, aIsHorizontal, &pref);
}
}
}
void
nsSplitterFrameInner::SetPreferredSize(nsBoxLayoutState& aState, nsIFrame* aChildBox, nscoord aOnePixel, bool aIsHorizontal, nscoord* aSize)
{
nsRect rect(aChildBox->GetRect());
nscoord pref = 0;
if (!aSize)
{
if (aIsHorizontal)
pref = rect.width;
else
pref = rect.height;
} else {
pref = *aSize;
}
nsMargin margin(0,0,0,0);
aChildBox->GetXULMargin(margin);
nsCOMPtr<nsIAtom> attribute;
if (aIsHorizontal) {
pref -= (margin.left + margin.right);
attribute = nsGkAtoms::width;
} else {
pref -= (margin.top + margin.bottom);
attribute = nsGkAtoms::height;
}
nsIContent* content = aChildBox->GetContent();
// set its preferred size.
nsAutoString prefValue;
prefValue.AppendInt(pref/aOnePixel);
if (content->AttrValueIs(kNameSpaceID_None, attribute,
prefValue, eCaseMatters))
return;
nsWeakFrame weakBox(aChildBox);
content->SetAttr(kNameSpaceID_None, attribute, prefValue, true);
ENSURE_TRUE(weakBox.IsAlive());
aState.PresShell()->FrameNeedsReflow(aChildBox, nsIPresShell::eStyleChange,
NS_FRAME_IS_DIRTY);
}
void
nsSplitterFrameInner::AddRemoveSpace(nscoord aDiff,
nsSplitterInfo* aChildInfos,
int32_t aCount,
int32_t& aSpaceLeft)
{
aSpaceLeft = 0;
for (int i=0; i < aCount; i++) {
nscoord min = aChildInfos[i].min;
nscoord max = aChildInfos[i].max;
nscoord& c = aChildInfos[i].changed;
// figure our how much space to add or remove
if (c + aDiff < min) {
aDiff += (c - min);
c = min;
} else if (c + aDiff > max) {
aDiff -= (max - c);
c = max;
} else {
c += aDiff;
aDiff = 0;
}
// there is not space left? We are done
if (aDiff == 0)
break;
}
aSpaceLeft = aDiff;
}
/**
* Ok if we want to resize a child we will know the actual size in pixels we want it to be.
* This is not the preferred size. But they only way we can change a child is my manipulating its
* preferred size. So give the actual pixel size this return method will return figure out the preferred
* size and set it.
*/
void
nsSplitterFrameInner::ResizeChildTo(nscoord& aDiff,
nsSplitterInfo* aChildrenBeforeInfos,
nsSplitterInfo* aChildrenAfterInfos,
int32_t aChildrenBeforeCount,
int32_t aChildrenAfterCount,
bool aBounded)
{
nscoord spaceLeft;
AddRemoveSpace(aDiff, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft);
// if there is any space left over remove it from the dif we were originally given
aDiff -= spaceLeft;
AddRemoveSpace(-aDiff, aChildrenAfterInfos,aChildrenAfterCount,spaceLeft);
if (spaceLeft != 0) {
if (aBounded) {
aDiff += spaceLeft;
AddRemoveSpace(spaceLeft, aChildrenBeforeInfos,aChildrenBeforeCount,spaceLeft);
}
}
}