From 0c562178929d239b21cc7194433c9bfa4541e573 Mon Sep 17 00:00:00 2001 From: Henrique Lorenzi Date: Fri, 20 Nov 2020 21:11:02 -0300 Subject: [PATCH] add window titles and preferred sizes --- index.html | 46 +---------------- src/MenuWindow.tsx | 6 +-- src/dockable/Container.tsx | 86 ++++++++++++++++++++++++++------ src/dockable/state.ts | 49 +++++++++++++++++- src/dockable/windowContext.ts | 37 ++++++++++++-- src/editor/EditorElement.tsx | 10 +++- src/editor/state_mouseDown.ts | 6 +-- src/popup/Button.tsx | 5 +- src/popup/Root.tsx | 35 +++++++------ src/windows/InstrumentSelect.tsx | 7 ++- src/windows/Timeline.tsx | 4 ++ src/windows/TrackSettings.tsx | 12 ++--- 12 files changed, 198 insertions(+), 105 deletions(-) diff --git a/index.html b/index.html index 562f17c..a891ab1 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ - Music Analysis + TheoryTracker @@ -42,50 +42,6 @@ background-color: #08f; color: #fff; } - - #canvasMain - { - width: 100vw; - height: calc(100vh - 16em); - } - - #divToolbox - { - width: 100vw; - height: 16em; - } - - .ribbonButton - { - background-color: #fff; - transition: background-color 0.2s, opacity 0.2s; - } - - .ribbonButton:hover - { - background-color: #eee; - } - - .ribbonButton:active - { - background-color: #ccc; - } - - .ribbonSlot - { - background-color: #fff; - transition: background-color 0.2s, opacity 0.2s; - } - - .ribbonSlot:hover - { - background-color: #eee; - } - - .ribbonButtonSelected - { - background-color: #f75 !important; - } diff --git a/src/MenuWindow.tsx b/src/MenuWindow.tsx index 7b574f4..1ef4a2d 100644 --- a/src/MenuWindow.tsx +++ b/src/MenuWindow.tsx @@ -14,11 +14,7 @@ export default function MenuWindow() const onOpenWindow = (elem: any, data: any) => { - dockable.ref.current.createFloating( - elem, data, - new Rect( - 100, 100, - 1, 1)) + dockable.ref.current.createFloating(elem, data) } diff --git a/src/dockable/Container.tsx b/src/dockable/Container.tsx index c2d399b..38fd0ae 100644 --- a/src/dockable/Container.tsx +++ b/src/dockable/Container.tsx @@ -26,6 +26,12 @@ const StyledCloseButton = styled.button` { border: 1px solid #fff; } + + &:active + { + border: 1px solid #fff; + background-color: #000; + } ` @@ -62,14 +68,37 @@ export function Container() const layoutRef = React.useRef(null!) + const rectRef = React.useRef(null!) + rectRef.current = rect layoutRef.current = React.useMemo(() => { return DockableData.getLayout(dockable.ref.current.state, rect) }, [rect, dockable.update]) + + React.useEffect(() => + { + let refreshed = false + for (const panel of dockable.ref.current.state.floatingPanels) + { + if (panel.justOpened) + { + panel.justOpened = false + panel.rect.w = panel.preferredFloatingSize.w + panel.rect.h = panel.preferredFloatingSize.h + Dockable.clampFloatingPanels(dockable.ref.current.state, rectRef.current) + refreshed = true + } + } + + if (refreshed) + dockable.commit() + + }, [dockable.update]) + - const mouseData = useMouseHandler(layoutRef) + const mouseData = useMouseHandler(layoutRef, rectRef) const anchorSize = 5 const anchorColor = "#0bf" @@ -223,7 +252,7 @@ function Panel(props: any) borderTopRightRadius: "0.5em", borderTopLeftRadius: "0.5em", }}> - { "Content " + cId } + { panelRect.panel.windowTitles[idx] || "New Window" } mouseHandler.onPanelTabClose(ev, panelRect.panel, idx)) as any } > @@ -242,9 +271,33 @@ function Panel(props: any) const data = !dockable.ref.current.contentIdToData ? null : dockable.ref.current.contentIdToData(cId) + const setTitle = (title: string) => + { + if (panelRect.panel.windowTitles[idx] != title) + { + panelRect.panel.windowTitles[idx] = title + dockable.commit() + } + } + + const setPreferredSize = (w: number, h: number) => + { + if (idx == panelRect.panel.curWindowIndex && + (w != panelRect.panel.preferredFloatingSize.w || + h != panelRect.panel.preferredFloatingSize.h)) + { + panelRect.panel.preferredFloatingSize = new Rect(0, 0, w, h) + dockable.commit() + } + } + return
): MouseHandlerData +function useMouseHandler( + layoutRef: React.MutableRefObject, + rectRef: React.MutableRefObject) + : MouseHandlerData { const dockableRef = Dockable.useDockable() @@ -415,7 +471,7 @@ function useMouseHandler(layoutRef: React.MutableRefObject): Mo if (state.mouseAction == MouseAction.MoveHeader) { - if (state.grabbedTab === null) + if (state.grabbedTab === null || state.grabbedPanel!.windowIds.length == 1) { if (!state.grabbedPanel!.floating) { @@ -430,12 +486,10 @@ function useMouseHandler(layoutRef: React.MutableRefObject): Mo Dockable.coallesceEmptyPanels(dockable) state.grabbedPanel!.rect = new Rect( - state.mousePos.x - 150, state.mousePos.y, - 300, 200) - - state.grabbedPanel!.rect = new Rect( - state.mousePos.x - 150, state.mousePos.y, - 300, 200) + state.mousePos.x - state.grabbedPanel!.preferredFloatingSize.w / 2, + state.mousePos.y, + state.grabbedPanel!.preferredFloatingSize.w, + state.grabbedPanel!.preferredFloatingSize.h) } } else @@ -447,8 +501,10 @@ function useMouseHandler(layoutRef: React.MutableRefObject): Mo Dockable.coallesceEmptyPanels(dockable) state.grabbedPanel!.rect = new Rect( - state.mousePos.x - 150, state.mousePos.y, - 300, 200) + state.mousePos.x - state.grabbedPanel!.preferredFloatingSize.w / 2, + state.mousePos.y, + state.grabbedPanel!.preferredFloatingSize.w, + state.grabbedPanel!.preferredFloatingSize.h) } bringToFront(state.grabbedPanel!) @@ -501,6 +557,8 @@ function useMouseHandler(layoutRef: React.MutableRefObject): Mo state.nearestAnchor.panel, state.nearestAnchor.mode) } + + Dockable.clampFloatingPanels(dockable, rectRef.current) } state.mouseDown = false @@ -528,7 +586,6 @@ function useMouseHandler(layoutRef: React.MutableRefObject): Mo const onPanelHeaderMouseDown = (ev: MouseEvent, panel: Dockable.Panel) => { ev.preventDefault() - ev.stopPropagation() const state = stateRef.ref.current state.grabbedPanel = panel state.grabbedTab = null @@ -562,7 +619,6 @@ function useMouseHandler(layoutRef: React.MutableRefObject): Mo const onPanelResize = (ev: MouseEvent, panel: Dockable.Panel) => { ev.preventDefault() - ev.stopPropagation() const state = stateRef.ref.current state.grabbedPanel = panel state.grabbedTab = null @@ -578,7 +634,6 @@ function useMouseHandler(layoutRef: React.MutableRefObject): Mo const onDividerResize = (ev: MouseEvent, divider: Dockable.Divider) => { ev.preventDefault() - ev.stopPropagation() const state = stateRef.ref.current state.grabbedDivider = divider state.mouseDown = true @@ -591,7 +646,6 @@ function useMouseHandler(layoutRef: React.MutableRefObject): Mo const onPanelTabClose = (ev: MouseEvent, panel: Dockable.Panel, tab: number) => { ev.preventDefault() - ev.stopPropagation() const dockable = dockableRef.ref.current.state const window = panel.windowIds[tab] Dockable.removeWindow(dockable, panel, window) diff --git a/src/dockable/state.ts b/src/dockable/state.ts index 9eb74b5..64db757 100644 --- a/src/dockable/state.ts +++ b/src/dockable/state.ts @@ -1,3 +1,4 @@ +import { Root } from "../project" import Rect from "../util/rect" @@ -41,6 +42,11 @@ export interface Panel splitPanels: Panel[] splitMode: SplitMode splitSize: number + + windowTitles: string[] + preferredFloatingSize: Rect + + justOpened: boolean } @@ -94,6 +100,11 @@ export function makeRoot(): State splitPanels: [], splitMode: SplitMode.LeftRight, splitSize: 0.5, + + windowTitles: [], + preferredFloatingSize: new Rect(0, 0, 300, 250), + + justOpened: false, }, floatingPanels: [], } @@ -113,6 +124,11 @@ export function makePanel(root: State): Panel splitPanels: [], splitMode: SplitMode.LeftRight, splitSize: 0.5, + + windowTitles: [], + preferredFloatingSize: new Rect(0, 0, 300, 250), + + justOpened: true, } root.floatingPanels.push(panel) return panel @@ -132,14 +148,20 @@ export function detachPanel(root: State, panel: Panel) export function addWindow(root: State, toPanel: Panel, window: WindowId) { toPanel.windowIds.push(window) + toPanel.windowTitles.push("") toPanel.curWindowIndex = toPanel.windowIds.length - 1 } export function removeWindow(root: State, fromPanel: Panel, window: WindowId) { - fromPanel.windowIds = fromPanel.windowIds.filter(w => w !== window) - fromPanel.curWindowIndex = 0 + const windowIndex = fromPanel.windowIds.findIndex(w => w === window) + if (windowIndex < 0) + return + + fromPanel.windowIds.splice(windowIndex, 1) + fromPanel.windowTitles.splice(windowIndex, 1) + fromPanel.curWindowIndex = Math.max(0, Math.min(fromPanel.windowIds.length - 1, fromPanel.curWindowIndex)) } @@ -180,6 +202,7 @@ export function dock(root: State, panel: Panel, dockIntoPanel: Panel, mode: Dock detachPanel(root, panel) panel.windowIds = [] + panel.windowTitles = [] root.floatingPanels = root.floatingPanels.filter(p => p !== panel) } else if (mode == DockMode.Right || @@ -199,11 +222,14 @@ export function dock(root: State, panel: Panel, dockIntoPanel: Panel, mode: Dock const newSubpanel = makePanel(root) newSubpanel.windowIds = dockIntoPanel.windowIds + newSubpanel.windowTitles = dockIntoPanel.windowTitles + newSubpanel.curWindowIndex = dockIntoPanel.curWindowIndex newSubpanel.splitMode = dockIntoPanel.splitMode newSubpanel.splitPanels = dockIntoPanel.splitPanels newSubpanel.splitSize = dockIntoPanel.splitSize dockIntoPanel.windowIds = [] + dockIntoPanel.windowTitles = [] dockIntoPanel.splitPanels = newSubpanels dockIntoPanel.splitMode = subdivMode dockIntoPanel.splitSize = subdivOriginalFirst ? 0.75 : 0.25 @@ -225,6 +251,25 @@ export function dock(root: State, panel: Panel, dockIntoPanel: Panel, mode: Dock } +export function clampFloatingPanels(root: State, rect: Rect) +{ + const margin = 10 + + for (const panel of root.floatingPanels) + { + panel.rect.x = + Math.max(rect.x + margin, + Math.min(rect.x2 - margin - panel.rect.w, + panel.rect.x)) + + panel.rect.y = + Math.max(rect.y + margin, + Math.min(rect.y2 - margin - panel.rect.h, + panel.rect.y)) + } +} + + export function traverseLayout(panel: Panel, rect: Rect, layout: Layout) { const xMid = (rect.x1 + rect.x2) / 2 diff --git a/src/dockable/windowContext.ts b/src/dockable/windowContext.ts index 1951cf9..3a0c992 100644 --- a/src/dockable/windowContext.ts +++ b/src/dockable/windowContext.ts @@ -13,7 +13,7 @@ export interface DockableContextProps contentIdToComponent: (id: Dockable.WindowId) => any contentIdToData: (id: Dockable.WindowId) => any - createFloating: (elem: any, data: any, rect: Rect) => void + createFloating: (elem: any, data: any, rect?: Rect) => void } @@ -27,8 +27,31 @@ export function useDockable(): RefState } +interface MousePos +{ + x: number + y: number +} + + export function useDockableInit(): RefState { + const mousePosRef = React.useRef({ x: 0, y: 0 }) + + React.useEffect(() => + { + const onMouseMove = (ev: MouseEvent) => + { + mousePosRef.current = { + x: ev.pageX, + y: ev.pageY, + } + } + + window.addEventListener("mousemove", onMouseMove) + + }, []) + const dockable = useRefState(() => { const idsToComponents = new Map() @@ -58,7 +81,7 @@ export function useDockableInit(): RefState return idsToData.get(id) } - const createFloating = (elem: any, data: any, rect: Rect) => + const createFloating = (elem: any, data: any, rect?: Rect) => { let state = dockable.ref.current.state @@ -68,7 +91,11 @@ export function useDockableInit(): RefState const panel = Dockable.makePanel(state) Dockable.addWindow(state, panel, id) - panel.rect = new Rect(rect.x2, rect.y2, 500, 300) + if (rect) + panel.rect = new Rect(rect.x2, rect.y2, 500, 300) + else + panel.rect = new Rect(mousePosRef.current.x, mousePosRef.current.y, 500, 300) + panel.bugfixAppearOnTop = true dockable.ref.current.state = state @@ -92,8 +119,12 @@ export function useDockableInit(): RefState export interface WindowProps { + panel: Dockable.Panel contentId: Dockable.WindowId data: any + + setTitle: (title: string) => void + setPreferredSize: (w: number, h: number) => void } diff --git a/src/editor/EditorElement.tsx b/src/editor/EditorElement.tsx index 613645b..1fc0de1 100644 --- a/src/editor/EditorElement.tsx +++ b/src/editor/EditorElement.tsx @@ -48,6 +48,8 @@ export function EditorElement(props: { state?: RefState }) const popup = Popup.usePopup() const dockable = Dockable.useDockable() + const lastTimelineRenderRef = React.useRef(0) + const makeUpdateData: () => EditorUpdateData = () => { return { @@ -66,7 +68,12 @@ export function EditorElement(props: { state?: RefState }) if (!refCanvas.current) return - console.log("render") + const now = new Date().getTime() + if (now < lastTimelineRenderRef.current + 15) + return + + console.log("Timeline render") + lastTimelineRenderRef.current = now const updateData = makeUpdateData() updateData.ctx = refCanvas.current.getContext("2d")! @@ -435,7 +442,6 @@ function TrackHeader(props: TrackHeaderProps) function TrackHeaderNoteBlocks(props: TrackHeaderProps) { - console.log("TrackHeaderNoteBlocks") const track = Project.getElem(props.project.ref.current, props.track.projectTrackId) if (!track || track.type != Project.ElementType.Track) return null diff --git a/src/editor/state_mouseDown.ts b/src/editor/state_mouseDown.ts index 49aea5f..a66f5a1 100644 --- a/src/editor/state_mouseDown.ts +++ b/src/editor/state_mouseDown.ts @@ -99,11 +99,7 @@ export function mouseDown(data: Editor.EditorUpdateData, rightButton: boolean) { data.dockable.ref.current.createFloating( Windows.TrackSettings, - { trackId: elem.id }, - new Rect( - data.state.renderRect.x + data.state.mouse.point.pos.x, - data.state.renderRect.y + data.state.mouse.point.pos.y, - 1, 1)) + { trackId: elem.id }) } } } diff --git a/src/popup/Button.tsx b/src/popup/Button.tsx index 515d11e..7a1f8e9 100644 --- a/src/popup/Button.tsx +++ b/src/popup/Button.tsx @@ -1,7 +1,7 @@ import React from "react" import Rect from "../util/rect" import { Root } from "./Root" -import { usePopupRoot } from "./popupContext" +import { usePopup, usePopupRoot } from "./popupContext" interface PopupButtonProps @@ -16,6 +16,7 @@ interface PopupButtonProps export function Button(props: PopupButtonProps) { const buttonRef = React.useRef(null) + const popupCtx = usePopup() const popupRootCtx = usePopupRoot() const [rect, setRect] = React.useState(new Rect(0, 0, 0, 0)) @@ -55,6 +56,8 @@ export function Button(props: PopupButtonProps) if (props.onClick) { props.onClick(ev) + popupCtx.ref.current.elem = null + popupCtx.commit() } else { diff --git a/src/popup/Root.tsx b/src/popup/Root.tsx index 9f923d7..b32854a 100644 --- a/src/popup/Root.tsx +++ b/src/popup/Root.tsx @@ -21,32 +21,32 @@ export function Root(props: PopupRootProps) const dismiss = (ev: any) => { - ev.preventDefault() popupCtx.ref.current.elem = null popupCtx.commit() } + React.useEffect(() => + { + const onClickVoid = (ev: MouseEvent) => + { + dismiss(ev) + } + + window.addEventListener("mousedown", onClickVoid) + return () => + { + window.removeEventListener("mousedown", onClickVoid) + } + }) + return
ev.preventDefault() } - onClick={ props.isSub ? undefined : dismiss } - //onMouseDown={ props.isSub ? undefined : dismiss } + onMouseDown={ ev => ev.stopPropagation() } style={{ - zIndex: 100000, - position: "fixed", - left: 0, - top: 0, - width: "100vw", - height: "100vh", - - backgroundColor: "transparent", - pointerEvents: props.isSub ? "none" : undefined, - }}> -
- { props.children } -
+ }}> + { props.children }
} \ No newline at end of file diff --git a/src/windows/InstrumentSelect.tsx b/src/windows/InstrumentSelect.tsx index c0470f8..5c5674d 100644 --- a/src/windows/InstrumentSelect.tsx +++ b/src/windows/InstrumentSelect.tsx @@ -41,8 +41,11 @@ export interface InstrumentSelectProps export function InstrumentSelect() { - const window = Dockable.useWindow() - const props: InstrumentSelectProps = window.data + const windowCtx = Dockable.useWindow() + windowCtx.setPreferredSize(500, 600) + windowCtx.setTitle("Instrument Select") + + const props: InstrumentSelectProps = windowCtx.data const onChangeType = (newType: string) => diff --git a/src/windows/Timeline.tsx b/src/windows/Timeline.tsx index 4d7c93c..a56a850 100644 --- a/src/windows/Timeline.tsx +++ b/src/windows/Timeline.tsx @@ -33,6 +33,10 @@ const StyledModeStackButton = styled.button` export function Timeline() { + const windowCtx = Dockable.useWindow() + windowCtx.setTitle("Timeline") + windowCtx.setPreferredSize(600, 450) + const editorState = useRefState(() => Editor.init()) const makeUpdateData: () => Editor.EditorUpdateData = () => diff --git a/src/windows/TrackSettings.tsx b/src/windows/TrackSettings.tsx index 556967f..dadd841 100644 --- a/src/windows/TrackSettings.tsx +++ b/src/windows/TrackSettings.tsx @@ -34,16 +34,19 @@ const StyledButton = styled.button` export function TrackSettings() { - const window = Dockable.useWindow() + const windowCtx = Dockable.useWindow() + windowCtx.setPreferredSize(500, 350) + const dockable = Dockable.useDockable() const project = Project.useProject() - const trackId: Project.ID = window.data.trackId + const trackId: Project.ID = windowCtx.data.trackId const elem = project.ref.current.elems.get(trackId) if (!elem) return null const track = elem as Project.Track + windowCtx.setTitle("Track [" + track.name + "]") const onRename = (newName: string) => { @@ -116,10 +119,7 @@ export function TrackSettings() dockable.ref.current.createFloating( Windows.InstrumentSelect, - { getInstrument, setInstrument }, - new Rect( - 100, 100, - 1, 1)) + { getInstrument, setInstrument }) } return