Merge pull request #2514 from matrix-org/bwindels/roomlistjslayout-bis

Redesign: new roomlist layout fixes
pull/21833/head
David Baker 2019-01-28 15:53:28 +00:00 committed by GitHub
commit 2d61a2508d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 51 deletions

View File

@ -19,9 +19,11 @@ limitations under the License.
/* take up remaining space below TopLeftMenu */ /* take up remaining space below TopLeftMenu */
flex: 1; flex: 1;
min-height: 0; min-height: 0;
overflow-y: hidden;
} }
.mx_RoomList .mx_ResizeHandle { .mx_RoomList .mx_ResizeHandle {
// needed so the z-index takes effect
position: relative; position: relative;
} }

View File

@ -19,11 +19,7 @@ import FixedDistributor from "./fixed";
// const allowWhitespace = true; // const allowWhitespace = true;
const handleHeight = 1; const handleHeight = 1;
function log() {
}
function clamp(height, min, max) { function clamp(height, min, max) {
//log(`clamping ${height} between ${min} and ${max}`);
if (height > max) return max; if (height > max) return max;
if (height < min) return min; if (height < min) return min;
return height; return height;
@ -31,9 +27,15 @@ function clamp(height, min, max) {
export class Layout { export class Layout {
constructor(applyHeight, initialSizes, collapsedState) { constructor(applyHeight, initialSizes, collapsedState) {
// callback to set height of section
this._applyHeight = applyHeight; this._applyHeight = applyHeight;
// list of {id, count} objects,
// determines sections and order of them
this._sections = []; this._sections = [];
// stores collapsed by id
this._collapsedState = Object.assign({}, collapsedState); this._collapsedState = Object.assign({}, collapsedState);
// total available height to the layout
// (including resize handles, ...)
this._availableHeight = 0; this._availableHeight = 0;
// heights stored by section section id // heights stored by section section id
this._sectionHeights = Object.assign({}, initialSizes); this._sectionHeights = Object.assign({}, initialSizes);
@ -58,28 +60,42 @@ export class Layout {
this._applyNewSize(); this._applyNewSize();
} }
// [{id, count}]
update(sections, availableHeight) { update(sections, availableHeight) {
if (Number.isFinite(availableHeight)) { let heightChanged = false;
if (Number.isFinite(availableHeight) && availableHeight !== this._availableHeight) {
heightChanged = true;
this._availableHeight = availableHeight; this._availableHeight = availableHeight;
} }
const sectionsChanged =
sections.length !== this._sections.length ||
sections.some((a, i) => {
const b = this._sections[i];
return a.id !== b.id || a.count !== b.count;
});
if (!heightChanged && !sectionsChanged) {
return;
}
this._sections = sections;
const totalHeight = this._getAvailableHeight(); const totalHeight = this._getAvailableHeight();
const defaultHeight = Math.floor(totalHeight / this._sections.length);
this._sections.forEach((section, i) => { this._sections.forEach((section, i) => {
if (!this._sectionHeights.hasOwnProperty(section.id)) { if (!this._sectionHeights[section.id]) {
this._sectionHeights[section.id] = clamp( this._sectionHeights[section.id] = clamp(
totalHeight / this._sections.length, defaultHeight,
this._getMinHeight(i), this._getMinHeight(i),
this._getMaxHeight(i), this._getMaxHeight(i),
); );
} }
}); });
this._sections = sections;
this._applyNewSize(); this._applyNewSize();
} }
openHandle(id) { openHandle(id) {
const index = this._getSectionIndex(id); const index = this._getSectionIndex(id);
//log(`openHandle resolved ${id} to ${index}`);
return new Handle(this, index, this._sectionHeights[id]); return new Handle(this, index, this._sectionHeights[id]);
} }
@ -128,12 +144,10 @@ export class Layout {
const section = this._sections[i]; const section = this._sections[i];
const collapsed = this._collapsedState[section.id]; const collapsed = this._collapsedState[section.id];
const maxItems = collapsed ? 0 : 1; const maxItems = collapsed ? 0 : 1;
// log("_getMinHeight", i, section);
return this._sectionHeight(Math.min(section.count, maxItems)); return this._sectionHeight(Math.min(section.count, maxItems));
} }
_applyOverflow(overflow, sections, blend) { _applyOverflow(overflow, sections, blend) {
//log("applyOverflow", overflow, sections);
// take the given overflow amount, and applies it to the given sections. // take the given overflow amount, and applies it to the given sections.
// calls itself recursively until it has distributed all the overflow // calls itself recursively until it has distributed all the overflow
// or run out of unclamped sections. // or run out of unclamped sections.
@ -155,10 +169,7 @@ export class Layout {
// 100 -= -100 // 100 -= -100
// 200 // 200
overflow -= this._heights[i] - newHeight; overflow -= this._heights[i] - newHeight;
// console.log(`this._heights[${i}] (${this._heights[i]}) - newHeight (${newHeight}) = ${this._heights[i] - newHeight}`);
// console.log(`changing ${this._heights[i]} to ${newHeight}`);
this._heights[i] = newHeight; this._heights[i] = newHeight;
// console.log(`for section ${i} overflow is ${overflow}`);
if (!blend) { if (!blend) {
overflowPerSection = overflow; overflowPerSection = overflow;
if (Math.abs(overflow) < 1.0) break; if (Math.abs(overflow) < 1.0) break;
@ -167,18 +178,16 @@ export class Layout {
if (Math.abs(overflow) > 1.0 && unclampedSections.length > 0) { if (Math.abs(overflow) > 1.0 && unclampedSections.length > 0) {
// we weren't able to distribute all the overflow so recurse and try again // we weren't able to distribute all the overflow so recurse and try again
// log("recursing with", overflow, unclampedSections);
overflow = this._applyOverflow(overflow, unclampedSections, blend); overflow = this._applyOverflow(overflow, unclampedSections, blend);
} }
return overflow; return overflow;
} }
_rebalanceAbove(anchor, overflowAbove) { _rebalanceAbove(sectionIndex, overflowAbove) {
if (Math.abs(overflowAbove) > 1.0) { if (Math.abs(overflowAbove) > 1.0) {
// log(`trying to rebalance upstream with ${overflowAbove}`);
const sections = []; const sections = [];
for (let i = anchor - 1; i >= 0; i--) { for (let i = sectionIndex - 1; i >= 0; i--) {
sections.push(i); sections.push(i);
} }
overflowAbove = this._applyOverflow(overflowAbove, sections); overflowAbove = this._applyOverflow(overflowAbove, sections);
@ -186,66 +195,58 @@ export class Layout {
return overflowAbove; return overflowAbove;
} }
_rebalanceBelow(anchor, overflowBelow) { _rebalanceBelow(sectionIndex, overflowBelow) {
if (Math.abs(overflowBelow) > 1.0) { if (Math.abs(overflowBelow) > 1.0) {
// log(`trying to rebalance downstream with ${overflowBelow}`);
const sections = []; const sections = [];
for (let i = anchor + 1; i < this._sections.length; i++) { for (let i = sectionIndex + 1; i < this._sections.length; i++) {
sections.push(i); sections.push(i);
} }
overflowBelow = this._applyOverflow(overflowBelow, sections); overflowBelow = this._applyOverflow(overflowBelow, sections);
//log(`rebalanced downstream with ${overflowBelow}`);
} }
return overflowBelow; return overflowBelow;
} }
// @param offset the amount the anchor is moved from what is stored in _sectionHeights, positive if downwards // @param offset the amount the sectionIndex is moved from what is stored in _sectionHeights, positive if downwards
// if we're clamped, return the offset we should be clamped at. // if we're constrained, return the offset we should be constrained at.
_relayout(anchor = 0, offset = 0, clamped = false) { _relayout(sectionIndex = 0, offset = 0, constrained = false) {
this._heights = this._sections.map((section) => this._sectionHeights[section.id]); this._heights = this._sections.map((section) => this._sectionHeights[section.id]);
// are these the amounts the items above/below shrank/grew and need to be relayouted? // are these the amounts the items above/below shrank/grew and need to be relayouted?
let overflowAbove; let overflowAbove;
let overflowBelow; let overflowBelow;
const maxHeight = this._getMaxHeight(anchor); const maxHeight = this._getMaxHeight(sectionIndex);
const minHeight = this._getMinHeight(anchor); const minHeight = this._getMinHeight(sectionIndex);
// new height > max ? // new height > max ?
if (this._heights[anchor] + offset > maxHeight) { if (this._heights[sectionIndex] + offset > maxHeight) {
// we're pulling downwards and clamped // we're pulling downwards and constrained
// overflowAbove = minus how much are we above max height? // overflowAbove = minus how much are we above max height
overflowAbove = (maxHeight - this._heights[anchor]) - offset; overflowAbove = (maxHeight - this._heights[sectionIndex]) - offset;
overflowBelow = offset; overflowBelow = offset;
// log(`pulling downwards clamped at max: ${overflowAbove} ${overflowBelow}`); } else if (this._heights[sectionIndex] + offset < minHeight) { // new height < min?
} else if (this._heights[anchor] + offset < minHeight) { // new height < min? // we're pulling upwards and constrained
// we're pulling upwards and clamped overflowAbove = (minHeight - this._heights[sectionIndex]) - offset;
// overflowAbove = ??? (offset is negative here, so - offset will add)
overflowAbove = (minHeight - this._heights[anchor]) - offset;
overflowBelow = offset; overflowBelow = offset;
// log(`pulling upwards clamped at min: ${overflowAbove} ${overflowBelow}`);
} else { } else {
overflowAbove = 0; overflowAbove = 0;
overflowBelow = offset; overflowBelow = offset;
// log(`resizing the anchor: ${overflowAbove} ${overflowBelow}`);
} }
this._heights[anchor] = clamp(this._heights[anchor] + offset, minHeight, maxHeight); this._heights[sectionIndex] = clamp(this._heights[sectionIndex] + offset, minHeight, maxHeight);
// these are reassigned the amount of overflow that could not be rebalanced // these are reassigned the amount of overflow that could not be rebalanced
// meaning we dragged the handle too far and it can't follow the cursor anymore // meaning we dragged the handle too far and it can't follow the cursor anymore
overflowAbove = this._rebalanceAbove(anchor, overflowAbove); overflowAbove = this._rebalanceAbove(sectionIndex, overflowAbove);
overflowBelow = this._rebalanceBelow(anchor, overflowBelow); overflowBelow = this._rebalanceBelow(sectionIndex, overflowBelow);
if (!clamped) { // to avoid risk of infinite recursion if (!constrained) { // to avoid risk of infinite recursion
// clamp to avoid overflowing or underflowing the page // clamp to avoid overflowing or underflowing the page
if (Math.abs(overflowAbove) > 1.0) { if (Math.abs(overflowAbove) > 1.0) {
// log(`clamping with overflowAbove ${overflowAbove}`);
// here we do the layout again with offset - the amount of space we took too much // here we do the layout again with offset - the amount of space we took too much
this._relayout(anchor, offset + overflowAbove, true); this._relayout(sectionIndex, offset + overflowAbove, true);
return offset + overflowAbove; return offset + overflowAbove;
} }
if (Math.abs(overflowBelow) > 1.0) { if (Math.abs(overflowBelow) > 1.0) {
// here we do the layout again with offset - the amount of space we took too much // here we do the layout again with offset - the amount of space we took too much
// log(`clamping with overflowBelow ${overflowBelow}`); this._relayout(sectionIndex, offset - overflowBelow, true);
this._relayout(anchor, offset - overflowBelow, true);
return offset - overflowBelow; return offset - overflowBelow;
} }
} }
@ -255,7 +256,6 @@ export class Layout {
} }
_applyHeights() { _applyHeights() {
log("updating layout, heights are now", this._heights);
// apply the heights // apply the heights
for (let i = 0; i < this._sections.length; i++) { for (let i = 0; i < this._sections.length; i++) {
const section = this._sections[i]; const section = this._sections[i];
@ -271,14 +271,14 @@ export class Layout {
} }
class Handle { class Handle {
constructor(layout, anchor, height) { constructor(layout, sectionIndex, height) {
this._layout = layout; this._layout = layout;
this._anchor = anchor; this._sectionIndex = sectionIndex;
this._initialHeight = height; this._initialHeight = height;
} }
setHeight(height) { setHeight(height) {
this._layout._relayout(this._anchor, height - this._initialHeight); this._layout._relayout(this._sectionIndex, height - this._initialHeight);
return this; return this;
} }