Adjusting channel tree size accordingly to the clients font size

master
WolverinDEV 2021-01-07 12:58:53 +01:00
parent fd590e5e10
commit 360b02a0c7
12 changed files with 126 additions and 94 deletions

View File

@ -1,4 +1,9 @@
# Changelog:
* **07.01.21**
- Improved general client ui memory footprint (Don't constantly rendering the channel tree)
- Improved channel tree loading performance especially on server join and switch
- The channel tree now adjusts accordingly to the clients font size
* **29.12.20**
- Reimplemented the music bot control UI
- Fixing some bugs from earlier versions

View File

@ -97,7 +97,7 @@
position: relative;
.handler {
padding-top: 4px;
padding-top: 5px;
position: relative;
flex-grow: 0;

View File

@ -1,4 +1,12 @@
.container {
width: 1em;
height: 1em;
display: flex;
flex-direction: column;
justify-content: stretch;
> img {
flex-grow: 1;
}
}

View File

@ -16,8 +16,8 @@ html:root {
cursor: pointer;
.containerArrow {
width: 16px;
margin-left: -16px;
width: 1em;
margin-left: -1em;
text-align: center;
flex-shrink: 0;
@ -25,15 +25,17 @@ html:root {
&.down {
align-self: normal;
}
> * {
font-size: .9em;
}
}
.channelType {
font-size: 16px;
flex-grow: 0;
flex-shrink: 0;
margin-right: 2px;
margin-right: .1em;;
}
.containerChannelName {
@ -47,7 +49,6 @@ html:root {
max-width: 100%; /* important for the repetitive channel name! */
overflow-x: hidden;
height: 16px;
&.align-right {
justify-content: right;
@ -58,6 +59,7 @@ html:root {
}
.channelName {
line-height: 1.2em;
align-self: center;
color: var(--channel-tree-entry-color);
@ -115,57 +117,49 @@ html:root {
}
}
height: 18px + 2px!important;
margin-top: -1px!important;
padding-top: 1px!important;
margin-bottom: -1px!important;
padding-bottom: 1px!important;
padding-left: 1px!important;
.leftPadding {
height: 100%;
margin-left: -1px;
}
&.drag-top {
height: 20px!important;
&::before {
content: "";
position: absolute;
padding-top: 0!important;
padding-bottom: 2px!important;
width: 100%;
top: -1px;
left: var(--drag-left-offset, 0);
border-top: 2px solid var(--channel-tree-move-border);
.leftPadding {
background-color: var(--channel-tree-background);
margin-top: -4px;
border-top: 2px solid var(--channel-tree-move-border);
}
}
&.drag-bottom {
padding-bottom: 0!important;
border-bottom: 2px solid var(--channel-tree-move-border);
z-index: 1;
.leftPadding {
background-color: var(--channel-tree-background);
margin-bottom: -3px;
&::after {
content: "";
position: absolute;
width: 100%;
bottom: -1px;
left: var(--drag-left-offset, 0);
border-bottom: 2px solid var(--channel-tree-move-border);
}
}
&.drag-contain {
padding-top: 0!important;
padding-bottom: 0!important;
padding-left: 0!important;
border: 1px solid var(--channel-tree-move-border);
border-radius: 2px;
z-index: 1;
&::before {
content: "";
position: absolute;
top: -1px;
bottom: -1px;
left: var(--drag-left-offset, 0);
right: 0;
margin-left: -.2em;
border: 2px solid var(--channel-tree-move-border);
}
}
}

View File

@ -15,17 +15,15 @@ html:root {
align-items: center;
> div {
margin-right: 2px;
margin-right: .1em;
}
.statusIcon {
flex-shrink: 0;
font-size: 16px;
}
.clientName {
line-height: 16px;
line-height: 1.2em;
min-width: 2em;
flex-grow: 0;

View File

@ -7,6 +7,7 @@ import {getIconManager} from "tc-shared/file/Icons";
import {Settings, settings} from "tc-shared/settings";
import {RDPChannel} from "tc-shared/ui/tree/RendererDataProvider";
import {UnreadMarkerRenderer} from "tc-shared/ui/tree/RendererTreeEntry";
import {ChannelTreeView} from "tc-shared/ui/tree/RendererView";
const channelStyle = require("./Channel.scss");
const viewStyle = require("./View.scss");
@ -132,7 +133,9 @@ export class RendererChannel extends React.Component<{ channel: RDPChannel }, {}
<div
ref={this.props.channel.refChannelContainer}
className={viewStyle.treeEntry + " " + channelStyle.channelEntry + " " + (channel.selected ? viewStyle.selected : "") + " " + dragClass}
style={{ top: channel.offsetTop }}
style={{
top: (channel.offsetTop * ChannelTreeView.EntryHeightEm) + "em",
}}
onMouseUp={event => {
if (event.button !== 0) {
return; /* only left mouse clicks */
@ -171,4 +174,21 @@ export class RendererChannel extends React.Component<{ channel: RDPChannel }, {}
</div>
);
}
componentDidUpdate(prevProps: Readonly<{ channel: RDPChannel }>, prevState: Readonly<{}>, snapshot?: any) {
this.fixCssVariables();
}
componentDidMount() {
this.fixCssVariables();
}
private fixCssVariables() {
const container = this.props.channel.refChannelContainer.current;
if(!container) {
return;
}
container.style.setProperty("--drag-left-offset", this.props.channel.offsetLeft + "em");
}
}

View File

@ -7,6 +7,7 @@ import {Settings, settings} from "tc-shared/settings";
import {UnreadMarkerRenderer} from "tc-shared/ui/tree/RendererTreeEntry";
import {RDPClient} from "tc-shared/ui/tree/RendererDataProvider";
import * as DOMPurify from "dompurify";
import {ChannelTreeView} from "tc-shared/ui/tree/RendererView";
const clientStyle = require("./Client.scss");
const viewStyle = require("./View.scss");
@ -176,7 +177,7 @@ export class RendererClient extends React.Component<{ client: RDPClient }, {}> {
return (
<div className={clientStyle.clientEntry + " " + viewStyle.treeEntry + " " + (selected ? viewStyle.selected : "")}
style={{ top: client.offsetTop }}
style={{ top: (client.offsetTop * ChannelTreeView.EntryHeightEm) + "em" }}
onContextMenu={event => {
if (settings.static(Settings.KEY_DISABLE_CONTEXT_MENU)) {
return;

View File

@ -329,6 +329,7 @@ export class RDPChannelTree {
this.selection = new RDPTreeSelection(this);
this.documentDragStopListener = () => {
return; /* FIXME: Remove! */
if(this.dragOverChannelEntry) {
this.dragOverChannelEntry.setDragHint("none");
this.dragOverChannelEntry = undefined;
@ -699,7 +700,7 @@ export class RDPChannelTree {
}
this.treeEntries[entry.entryId] = result;
result.handlePositionUpdate(index * ChannelTreeView.EntryHeight, entry.depth);
result.handlePositionUpdate(index, entry.depth);
return result;
}).filter(e => !!e);
@ -734,8 +735,8 @@ export abstract class RDPEntry {
readonly refUnread = React.createRef<UnreadMarkerRenderer>();
offsetTop: number;
offsetLeft: number;
offsetTop: number; /* In 16px units */
offsetLeft: number; /* In channel units */
selected: boolean = false;
unread: boolean = false;

View File

@ -5,6 +5,9 @@ import {UnreadMarkerRenderer} from "./RendererTreeEntry";
import {getIconManager} from "tc-shared/file/Icons";
import {RDPServer} from "tc-shared/ui/tree/RendererDataProvider";
import {Translatable, VariadicTranslatable} from "tc-shared/ui/react-elements/i18n";
import {ChannelTreeView} from "tc-shared/ui/tree/RendererView";
import {ClientIconRenderer} from "tc-shared/ui/react-elements/Icons";
import {ClientIcon} from "svg-sprites/client-icons";
const serverStyle = require("./Server.scss");
const viewStyle = require("./View.scss");
@ -37,7 +40,7 @@ export class ServerRenderer extends React.Component<{ server: RDPServer }, {}> {
return (
<div
className={serverStyle.serverEntry + " " + viewStyle.treeEntry + " " + (selected ? viewStyle.selected : "")}
style={{ top: server.offsetTop }}
style={{ top: (server.offsetTop * ChannelTreeView.EntryHeightEm) + "em" }}
onMouseDown={event => {
if (event.button !== 0) {
return; /* only left mouse clicks */
@ -60,7 +63,7 @@ export class ServerRenderer extends React.Component<{ server: RDPServer }, {}> {
>
<div className={viewStyle.leftPadding} style={{ paddingLeft: server.offsetLeft + "em" }} />
<UnreadMarkerRenderer entry={server} ref={server.refUnread} />
<div className={"icon client-server_green " + serverStyle.server_type}/>
<ClientIconRenderer icon={ClientIcon.ServerGreen} className={serverStyle.icon} />
<div className={serverStyle.name}>{name}</div>
{icon}
</div>

View File

@ -36,11 +36,12 @@ export interface ChannelTreeViewProperties {
}
export interface ChannelTreeViewState {
element_scroll_offset?: number; /* in px */
scroll_offset: number; /* in px */
view_height: number; /* in px */
elementScrollOffset?: number; /* in px */
scrollOffset: number; /* in px */
viewHeight: number; /* in px */
fontSize: number; /* in px */
tree_version: number;
treeVersion: number;
smoothScroll: boolean;
/* the currently rendered tree */
@ -51,7 +52,7 @@ export interface ChannelTreeViewState {
@ReactEventHandler<ChannelTreeView>(e => e.props.events)
@BatchUpdateAssignment(BatchUpdateType.CHANNEL_TREE)
export class ChannelTreeView extends ReactComponentBase<ChannelTreeViewProperties, ChannelTreeViewState> {
public static readonly EntryHeight = 18;
public static readonly EntryHeightEm = 1.3;
private readonly refContainer = React.createRef<HTMLDivElement>();
private resizeObserver: ResizeObserver;
@ -68,12 +69,13 @@ export class ChannelTreeView extends ReactComponentBase<ChannelTreeViewPropertie
super(props);
this.state = {
scroll_offset: 0,
view_height: 0,
tree_version: 0,
scrollOffset: 0,
viewHeight: 0,
treeVersion: 0,
smoothScroll: false,
tree: [],
treeRevision: -1
treeRevision: -1,
fontSize: 14
};
}
@ -89,11 +91,17 @@ export class ChannelTreeView extends ReactComponentBase<ChannelTreeViewPropertie
}
const bounds = entries[0].contentRect;
if (this.state.view_height !== bounds.height) {
if (this.state.viewHeight !== bounds.height) {
this.setState({
view_height: bounds.height
viewHeight: bounds.height
});
}
const fontSize = parseFloat(getComputedStyle(entries[0].target).getPropertyValue("font-size"));
console.error("Updated font size to: %o", fontSize);
this.setState({
fontSize: fontSize || 0
});
});
this.resizeObserver.observe(this.refContainer.current);
@ -119,14 +127,15 @@ export class ChannelTreeView extends ReactComponentBase<ChannelTreeViewPropertie
this.scrollFixRequested = true;
requestAnimationFrame(() => {
this.scrollFixRequested = false;
this.refContainer.current.scrollTop = this.state.scroll_offset;
this.refContainer.current.scrollTop = this.state.scrollOffset;
this.setState({smoothScroll: true});
});
}
private visibleEntries() {
let viewEntryCount = Math.ceil(this.state.view_height / ChannelTreeView.EntryHeight);
const viewEntryBegin = Math.floor(this.state.scroll_offset / ChannelTreeView.EntryHeight);
const entryHeight = ChannelTreeView.EntryHeightEm * this.state.fontSize;
let viewEntryCount = Math.ceil(this.state.viewHeight / entryHeight);
const viewEntryBegin = Math.floor(this.state.scrollOffset / entryHeight);
const viewEntryEnd = Math.min(this.state.tree.length, viewEntryBegin + viewEntryCount);
return {
@ -170,7 +179,7 @@ export class ChannelTreeView extends ReactComponentBase<ChannelTreeViewPropertie
>
<div
className={viewStyle.channelTree}
style={{height: (this.state.tree.length * ChannelTreeView.EntryHeight) + "px"}}>
style={{height: (this.state.tree.length * ChannelTreeView.EntryHeightEm) + "em"}}>
{elements}
</div>
</div>
@ -179,7 +188,7 @@ export class ChannelTreeView extends ReactComponentBase<ChannelTreeViewPropertie
private onScroll() {
this.setState({
scroll_offset: this.refContainer.current.scrollTop
scrollOffset: this.refContainer.current.scrollTop
});
}
@ -191,18 +200,18 @@ export class ChannelTreeView extends ReactComponentBase<ChannelTreeViewPropertie
return;
}
let new_index;
let newIndex;
const currentRange = this.visibleEntries();
if (index >= currentRange.end - 1) {
new_index = index - (currentRange.end - currentRange.begin) + 2;
newIndex = index - (currentRange.end - currentRange.begin) + 2;
} else if (index < currentRange.begin) {
new_index = index;
newIndex = index;
} else {
if (callback) callback();
return;
}
this.refContainer.current.scrollTop = new_index * ChannelTreeView.EntryHeight;
this.refContainer.current.scrollTop = newIndex * ChannelTreeView.EntryHeightEm * this.state.fontSize;
if (callback) {
let cb = {
@ -211,7 +220,7 @@ export class ChannelTreeView extends ReactComponentBase<ChannelTreeViewPropertie
timeout: setTimeout(() => {
this.inViewCallbacks.remove(cb);
callback();
}, (Math.abs(new_index - currentRange.begin) / (currentRange.end - currentRange.begin)) * 1500)
}, (Math.abs(newIndex - currentRange.begin) / (currentRange.end - currentRange.begin)) * 1500)
};
this.inViewCallbacks.push(cb);
}
@ -233,7 +242,7 @@ export class ChannelTreeView extends ReactComponentBase<ChannelTreeViewPropertie
return undefined;
}
const total_offset = container.scrollTop + pageY;
return this.state.tree[Math.floor(total_offset / ChannelTreeView.EntryHeight)]?.entryId;
const totalOffset = container.scrollTop + pageY;
return this.state.tree[Math.floor(totalOffset / (ChannelTreeView.EntryHeightEm * this.state.fontSize))]?.entryId;
}
}

View File

@ -30,4 +30,10 @@
overflow: hidden;
text-overflow: ellipsis;
}
.icon {
align-self: center;
margin-bottom: .1em;
margin-right: .1em;
}
}

View File

@ -25,15 +25,10 @@ html:root {
* {
font-family: sans-serif;
font-size: 12px;
white-space: pre;
line-height: 1;
}
:global(.icon_em) {
font-size: 16px;
}
.treeEntry {
position: absolute;
left: 0;
@ -43,7 +38,7 @@ html:root {
flex-direction: row;
justify-content: stretch;
height: 18px;
height: 1.3em;
padding-top: 1px;
padding-bottom: 1px;
@ -89,18 +84,10 @@ html:root {
@include transition(opacity $button_hover_animation_time);
}
:global(.icon-container) {
height: 16px;
width: 16px;
}
}
.leftPadding {
flex-shrink: 0;
padding-left: 2px;
font-size: 16px;
}
&.move {
@ -205,7 +192,7 @@ html:root {
@media all and (max-width: 250px) {
.channelTree .leftPadding {
font-size: 6px;
font-size: .4em;
}
.treeEntry {