mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
Add gap size setting (#325)
Adds a new setting for the gap size between tiles in a layout. Also updates the resize handle calculations so they are dynamically generated based on the gap size. Also updates the styling for the resize handles to be more robust. This also updates the default gap size to 3px. This also slims out the Block Frame padding so it is just enough that the blocks don't overlap when there's no gap.
This commit is contained in:
parent
072730f7eb
commit
74c8044c73
@ -91,7 +91,6 @@ function AppSettingsUpdater() {
|
||||
(windowSettings?.["window:transparent"] || windowSettings?.["window:blur"]) ?? false;
|
||||
const opacity = util.boundNumber(windowSettings?.["window:opacity"] ?? 0.8, 0, 1);
|
||||
let baseBgColor = windowSettings?.["window:bgcolor"];
|
||||
console.log("window settings", windowSettings);
|
||||
if (isTransparentOrBlur) {
|
||||
document.body.classList.add("is-transparent");
|
||||
const rootStyles = getComputedStyle(document.documentElement);
|
||||
|
@ -62,7 +62,7 @@
|
||||
|
||||
&.block-frame-default {
|
||||
position: relative;
|
||||
padding: 2px;
|
||||
padding: 1px;
|
||||
|
||||
.block-frame-default-inner {
|
||||
background-color: var(--block-bg-color);
|
||||
|
@ -360,7 +360,6 @@ function handleWSEventMessage(msg: WSEventType) {
|
||||
}
|
||||
if (msg.eventtype == "config") {
|
||||
const fullConfig = (msg.data as WatcherUpdate).fullconfig;
|
||||
console.log("fullConfig", fullConfig);
|
||||
globalStore.set(atoms.fullConfigAtom, fullConfig);
|
||||
return;
|
||||
}
|
||||
|
@ -4,23 +4,30 @@
|
||||
import { Block } from "@/app/block/block";
|
||||
import { CenteredDiv } from "@/element/quickelems";
|
||||
import { ContentRenderer, NodeModel, PreviewRenderer, TileLayout } from "@/layout/index";
|
||||
import { getApi } from "@/store/global";
|
||||
import { TileLayoutContents } from "@/layout/lib/types";
|
||||
import { atoms, getApi } from "@/store/global";
|
||||
import * as services from "@/store/services";
|
||||
import * as WOS from "@/store/wos";
|
||||
import { useAtomValue } from "jotai";
|
||||
import { atom, useAtomValue } from "jotai";
|
||||
import * as React from "react";
|
||||
import { useMemo } from "react";
|
||||
import "./tabcontent.less";
|
||||
|
||||
const tileGapSizeAtom = atom((get) => {
|
||||
const settings = get(atoms.settingsAtom);
|
||||
return settings["window:tilegapsize"];
|
||||
});
|
||||
|
||||
const TabContent = React.memo(({ tabId }: { tabId: string }) => {
|
||||
const oref = useMemo(() => WOS.makeORef("tab", tabId), [tabId]);
|
||||
const loadingAtom = useMemo(() => WOS.getWaveObjectLoadingAtom(oref), [oref]);
|
||||
const tabLoading = useAtomValue(loadingAtom);
|
||||
const tabAtom = useMemo(() => WOS.getWaveObjectAtom<Tab>(oref), [oref]);
|
||||
const tabData = useAtomValue(tabAtom);
|
||||
const tileGapSize = useAtomValue(tileGapSizeAtom);
|
||||
|
||||
const tileLayoutContents = useMemo(() => {
|
||||
const renderBlock: ContentRenderer = (nodeModel: NodeModel) => {
|
||||
const renderContent: ContentRenderer = (nodeModel: NodeModel) => {
|
||||
return <Block key={nodeModel.blockId} nodeModel={nodeModel} preview={false} />;
|
||||
};
|
||||
|
||||
@ -33,12 +40,13 @@ const TabContent = React.memo(({ tabId }: { tabId: string }) => {
|
||||
}
|
||||
|
||||
return {
|
||||
renderContent: renderBlock,
|
||||
renderPreview: renderPreview,
|
||||
tabId: tabId,
|
||||
onNodeDelete: onNodeDelete,
|
||||
};
|
||||
}, [tabId]);
|
||||
renderContent,
|
||||
renderPreview,
|
||||
tabId,
|
||||
onNodeDelete,
|
||||
gapSizePx: tileGapSize,
|
||||
} as TileLayoutContents;
|
||||
}, [tabId, tileGapSize]);
|
||||
|
||||
if (tabLoading) {
|
||||
return (
|
||||
|
@ -75,7 +75,6 @@ interface CodeEditorProps {
|
||||
|
||||
const minimapEnabledAtom = atom((get) => {
|
||||
const settings = get(atoms.settingsAtom);
|
||||
console.log("settings", settings);
|
||||
return settings["editor:minimapenabled"] ?? false;
|
||||
});
|
||||
|
||||
@ -108,7 +107,6 @@ export function CodeEditor({ text, language, filename, onChange, onMount }: Code
|
||||
|
||||
const editorOpts = useMemo(() => {
|
||||
const opts = defaultEditorOptions();
|
||||
console.log("minimapEnabled", minimapEnabled);
|
||||
opts.minimap.enabled = minimapEnabled;
|
||||
return opts;
|
||||
}, [minimapEnabled]);
|
||||
|
@ -101,13 +101,15 @@ function TileLayoutComponent({ tabAtom, contents, getCursorPoint }: TileLayoutPr
|
||||
}, 50);
|
||||
}, []);
|
||||
|
||||
const gapSizePx = useAtomValue(layoutModel.gapSizePx);
|
||||
const animationTimeS = useAtomValue(layoutModel.animationTimeS);
|
||||
const tileStyle = useMemo(
|
||||
() =>
|
||||
({
|
||||
"--gap-size-px": `${layoutModel.gapSizePx}px`,
|
||||
"--animation-time-s": `${layoutModel.animationTimeS}s`,
|
||||
"--gap-size-px": `${gapSizePx}px`,
|
||||
"--animation-time-s": `${animationTimeS}s`,
|
||||
}) as CSSProperties,
|
||||
[layoutModel.gapSizePx, layoutModel.animationTimeS]
|
||||
[gapSizePx, animationTimeS]
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -56,7 +56,7 @@ interface ResizeContext {
|
||||
afterNodeStartSize: number;
|
||||
}
|
||||
|
||||
const DefaultGapSizePx = 5;
|
||||
const DefaultGapSizePx = 3;
|
||||
const MinNodeSizePx = 40;
|
||||
const DefaultAnimationTimeS = 0.15;
|
||||
|
||||
@ -96,12 +96,12 @@ export class LayoutModel {
|
||||
/**
|
||||
* The size of the gap between nodes in CSS pixels.
|
||||
*/
|
||||
gapSizePx: number;
|
||||
gapSizePx: PrimitiveAtom<number>;
|
||||
|
||||
/**
|
||||
* The time a transition animation takes, in seconds.
|
||||
*/
|
||||
animationTimeS: number;
|
||||
animationTimeS: PrimitiveAtom<number>;
|
||||
|
||||
/**
|
||||
* List of nodes that are leafs and should be rendered as a DisplayNode.
|
||||
@ -183,13 +183,7 @@ export class LayoutModel {
|
||||
* The resize handle size is double the gap size, or double the default gap size, whichever is greater.
|
||||
* @see gapSizePx @see DefaultGapSizePx
|
||||
*/
|
||||
private resizeHandleSizePx: number;
|
||||
/**
|
||||
* Half of the size of the resize handles, in CSS pixels.
|
||||
*
|
||||
* @see resizeHandleSizePx This is just a precomputed halving of the resize handle size.
|
||||
*/
|
||||
private halfResizeHandleSizePx: number;
|
||||
private resizeHandleSizePx: Atom<number>;
|
||||
/**
|
||||
* A context used by the resize handles to keep track of precomputed values for the current resize operation.
|
||||
*/
|
||||
@ -219,10 +213,12 @@ export class LayoutModel {
|
||||
this.renderContent = renderContent;
|
||||
this.renderPreview = renderPreview;
|
||||
this.onNodeDelete = onNodeDelete;
|
||||
this.gapSizePx = gapSizePx ?? DefaultGapSizePx;
|
||||
this.halfResizeHandleSizePx = this.gapSizePx > 5 ? this.gapSizePx : DefaultGapSizePx;
|
||||
this.resizeHandleSizePx = 2 * this.halfResizeHandleSizePx;
|
||||
this.animationTimeS = animationTimeS ?? DefaultAnimationTimeS;
|
||||
this.gapSizePx = atom(gapSizePx ?? DefaultGapSizePx);
|
||||
this.resizeHandleSizePx = atom((get) => {
|
||||
const gapSizePx = get(this.gapSizePx);
|
||||
return 2 * (gapSizePx > 5 ? gapSizePx : DefaultGapSizePx);
|
||||
});
|
||||
this.animationTimeS = atom(animationTimeS ?? DefaultAnimationTimeS);
|
||||
this.lastTreeStateGeneration = -1;
|
||||
|
||||
this.leafs = atom([]);
|
||||
@ -292,9 +288,14 @@ export class LayoutModel {
|
||||
* @param contents Contains callbacks provided by the TileLayout component.
|
||||
*/
|
||||
registerTileLayout(contents: TileLayoutContents) {
|
||||
console.log("registerTileLayout", contents);
|
||||
this.renderContent = contents.renderContent;
|
||||
this.renderPreview = contents.renderPreview;
|
||||
this.onNodeDelete = contents.onNodeDelete;
|
||||
if (contents.gapSizePx !== undefined) {
|
||||
console.log("setting gapSizePx", contents.gapSizePx);
|
||||
this.setter(this.gapSizePx, contents.gapSizePx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -470,8 +471,9 @@ export class LayoutModel {
|
||||
pendingAction?.type === LayoutTreeActionType.ResizeNode
|
||||
? (pendingAction as LayoutTreeResizeNodeAction)
|
||||
: null;
|
||||
const resizeHandleSizePx = this.getter(this.resizeHandleSizePx);
|
||||
const callback = (node: LayoutNode) =>
|
||||
this.updateTreeHelper(node, newAdditionalProps, newLeafs, resizeAction);
|
||||
this.updateTreeHelper(node, newAdditionalProps, newLeafs, resizeHandleSizePx, resizeAction);
|
||||
if (balanceTree) this.treeState.rootNode = balanceNode(this.treeState.rootNode, callback);
|
||||
else walkNodes(this.treeState.rootNode, callback);
|
||||
|
||||
@ -499,6 +501,7 @@ export class LayoutModel {
|
||||
node: LayoutNode,
|
||||
additionalPropsMap: Record<string, LayoutNodeAdditionalProps>,
|
||||
leafs: LayoutNode[],
|
||||
resizeHandleSizePx: number,
|
||||
resizeAction?: LayoutTreeResizeNodeAction
|
||||
) {
|
||||
/**
|
||||
@ -567,15 +570,16 @@ export class LayoutModel {
|
||||
// We only want the resize handles in between nodes, this ensures we have n-1 handles.
|
||||
if (lastChildRect) {
|
||||
const resizeHandleIndex = resizeHandles.length;
|
||||
const halfResizeHandleSizePx = resizeHandleSizePx / 2;
|
||||
const resizeHandleDimensions: Dimensions = {
|
||||
top: nodeIsRow
|
||||
? lastChildRect.top
|
||||
: lastChildRect.top + lastChildRect.height - this.halfResizeHandleSizePx,
|
||||
: lastChildRect.top + lastChildRect.height - halfResizeHandleSizePx,
|
||||
left: nodeIsRow
|
||||
? lastChildRect.left + lastChildRect.width - this.halfResizeHandleSizePx
|
||||
? lastChildRect.left + lastChildRect.width - halfResizeHandleSizePx
|
||||
: lastChildRect.left,
|
||||
width: nodeIsRow ? this.resizeHandleSizePx : lastChildRect.width,
|
||||
height: nodeIsRow ? lastChildRect.height : this.resizeHandleSizePx,
|
||||
width: nodeIsRow ? resizeHandleSizePx : lastChildRect.width,
|
||||
height: nodeIsRow ? lastChildRect.height : resizeHandleSizePx,
|
||||
};
|
||||
resizeHandles.push({
|
||||
id: `${node.id}-${resizeHandleIndex}`,
|
||||
@ -584,8 +588,7 @@ export class LayoutModel {
|
||||
transform: setTransform(resizeHandleDimensions, true, false),
|
||||
flexDirection: node.flexDirection,
|
||||
centerPx:
|
||||
(nodeIsRow ? resizeHandleDimensions.left : resizeHandleDimensions.top) +
|
||||
this.halfResizeHandleSizePx,
|
||||
(nodeIsRow ? resizeHandleDimensions.left : resizeHandleDimensions.top) + halfResizeHandleSizePx,
|
||||
});
|
||||
}
|
||||
lastChildRect = rect;
|
||||
@ -750,7 +753,6 @@ export class LayoutModel {
|
||||
if (!this.nodeModels.has(nodeid)) {
|
||||
this.nodeModels.set(nodeid, {
|
||||
additionalProps: addlPropsAtom,
|
||||
animationTimeS: this.animationTimeS,
|
||||
innerRect: atom((get) => {
|
||||
const addlProps = get(addlPropsAtom);
|
||||
const numLeafs = get(this.numLeafs);
|
||||
|
@ -49,14 +49,14 @@
|
||||
cursor: ew-resize;
|
||||
.line {
|
||||
height: 100%;
|
||||
margin-left: calc(var(--gap-size-px) - 1px);
|
||||
border-left: 2px solid var(--accent-color);
|
||||
width: calc(50% + 1px);
|
||||
border-right: 2px solid var(--accent-color);
|
||||
}
|
||||
}
|
||||
&.flex-column {
|
||||
cursor: ns-resize;
|
||||
.line {
|
||||
margin-top: calc(var(--gap-size-px) - 1px);
|
||||
height: calc(50% + 1px);
|
||||
border-bottom: 2px solid var(--accent-color);
|
||||
}
|
||||
}
|
||||
@ -80,7 +80,6 @@
|
||||
}
|
||||
|
||||
&.magnified {
|
||||
border-radius: calc(var(--block-border-radius) + 4px);
|
||||
background-color: var(--block-bg-solid-color);
|
||||
z-index: var(--zindex-layout-magnified-node);
|
||||
}
|
||||
@ -110,8 +109,11 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&:not(:only-child) .tile-leaf {
|
||||
padding: calc(var(--gap-size-px) / 2);
|
||||
&:not(:only-child),
|
||||
&:not(.magnified) {
|
||||
.tile-leaf {
|
||||
padding: calc(var(--gap-size-px) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,6 +274,21 @@ export const DefaultNodeSize = 10;
|
||||
* nothing in here is specific to the TileLayout itself
|
||||
*/
|
||||
export interface TileLayoutContents {
|
||||
/**
|
||||
* The tabId with which this TileLayout is associated.
|
||||
*/
|
||||
tabId?: string;
|
||||
|
||||
/**
|
||||
* The class name to use for the top-level div of the tile layout.
|
||||
*/
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* The gap between tiles in a layout, in CSS pixels.
|
||||
*/
|
||||
gapSizePx?: number;
|
||||
|
||||
/**
|
||||
* A callback that accepts the data from the leaf node and displays the leaf contents to the user.
|
||||
*/
|
||||
@ -287,21 +302,11 @@ export interface TileLayoutContents {
|
||||
* @param data The contents of the node that was deleted.
|
||||
*/
|
||||
onNodeDelete?: (data: TabLayoutData) => Promise<void>;
|
||||
/**
|
||||
* The class name to use for the top-level div of the tile layout.
|
||||
*/
|
||||
className?: string;
|
||||
|
||||
/**
|
||||
* A callback for getting the cursor point in reference to the current window. This removes Electron as a runtime dependency, allowing for better integration with Storybook.
|
||||
* @returns The cursor position relative to the current window.
|
||||
*/
|
||||
getCursorPoint?: () => Point;
|
||||
|
||||
/**
|
||||
* tabId this TileLayout is associated with
|
||||
*/
|
||||
tabId?: string;
|
||||
}
|
||||
|
||||
export interface ResizeHandleProps {
|
||||
@ -325,7 +330,6 @@ export interface LayoutNodeAdditionalProps {
|
||||
|
||||
export interface NodeModel {
|
||||
additionalProps: Atom<LayoutNodeAdditionalProps>;
|
||||
animationTimeS: number;
|
||||
innerRect: Atom<CSSProperties>;
|
||||
blockNum: Atom<number>;
|
||||
numLeafs: Atom<number>;
|
||||
|
1
frontend/types/gotypes.d.ts
vendored
1
frontend/types/gotypes.d.ts
vendored
@ -412,6 +412,7 @@ declare global {
|
||||
"window:opacity"?: number;
|
||||
"window:bgcolor"?: string;
|
||||
"window:reducedmotion"?: boolean;
|
||||
"window:tilegapsize": number;
|
||||
"telemetry:*"?: boolean;
|
||||
"telemetry:enabled"?: boolean;
|
||||
};
|
||||
|
@ -5,5 +5,6 @@
|
||||
"autoupdate:enabled": true,
|
||||
"autoupdate:installonquit": true,
|
||||
"autoupdate:intervalms": 3600000,
|
||||
"editor:minimapenabled": true
|
||||
"editor:minimapenabled": true,
|
||||
"window:tilegapsize": 3
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ const (
|
||||
ConfigKey_WindowOpacity = "window:opacity"
|
||||
ConfigKey_WindowBgColor = "window:bgcolor"
|
||||
ConfigKey_WindowReducedMotion = "window:reducedmotion"
|
||||
ConfigKey_WindowTileGapSize = "window:tilegapsize"
|
||||
|
||||
ConfigKey_TelemetryClear = "telemetry:*"
|
||||
ConfigKey_TelemetryEnabled = "telemetry:enabled"
|
||||
|
@ -73,6 +73,7 @@ type SettingsType struct {
|
||||
WindowOpacity float64 `json:"window:opacity,omitempty"`
|
||||
WindowBgColor string `json:"window:bgcolor,omitempty"`
|
||||
WindowReducedMotion bool `json:"window:reducedmotion,omitempty"`
|
||||
WindowTileGapSize int8 `json:"window:tilegapsize"`
|
||||
|
||||
TelemetryClear bool `json:"telemetry:*,omitempty"`
|
||||
TelemetryEnabled bool `json:"telemetry:enabled,omitempty"`
|
||||
|
Loading…
Reference in New Issue
Block a user