nsSplitterFrame.cpp 31 KB

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