From 2048a89d7ebd4b724991829e9922253c87b79866 Mon Sep 17 00:00:00 2001 From: mattuy Date: Wed, 6 Oct 2021 18:14:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E4=BD=93=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/EditableContent/index.js | 2 +- src/menu/editor.js | 43 +++++++++----- src/menu/notation.js | 76 +++++-------------------- src/menu/paragraph.js | 2 + src/store/history.js | 9 +-- src/util/editor.js | 18 ++---- src/util/file.js | 9 +-- src/util/paragraph.js | 15 ++++- src/util/placement.js | 22 ++----- src/view/ConfigModal/index.js | 45 +++++++++------ src/view/Editor/index.js | 28 ++++----- src/view/Header/LeftInfoBlock/index.js | 1 - src/view/Header/RightInfoBlock/index.js | 1 - src/view/Header/Title/index.js | 3 +- src/view/HelpModal/index.js | 2 - src/view/Notation/index.js | 11 ++-- src/view/Paragraph/index.js | 6 +- 17 files changed, 118 insertions(+), 175 deletions(-) diff --git a/src/component/EditableContent/index.js b/src/component/EditableContent/index.js index c847879..5e81a2f 100644 --- a/src/component/EditableContent/index.js +++ b/src/component/EditableContent/index.js @@ -104,7 +104,7 @@ const EditableContent = function ( ))} ); - }, [initialValue, options, handleConfirm]); + }, [initialValue, options, handleConfirm, handleHideContextMenu, overlay]); let renderContent, renderPopover; switch (inputType) { case "select": diff --git a/src/menu/editor.js b/src/menu/editor.js index 5f935fd..f254a57 100644 --- a/src/menu/editor.js +++ b/src/menu/editor.js @@ -1,4 +1,3 @@ -import { Button, Menu, Upload } from "antd"; import { FolderOpenOutlined, HighlightOutlined, @@ -8,10 +7,14 @@ import { SaveOutlined, UndoOutlined, } from "@ant-design/icons"; +import { Menu } from "antd"; import state from "../store/state"; import store from "../store/global"; import { createNotation, isNote, notations as N } from "../util/notation"; -import { createParagraph } from "../util/paragraph"; +import { + createParagraph, + createParagraphWithNotations, +} from "../util/paragraph"; import { exportFile, loadFile, saveFile } from "../util/file"; import { findParagraphAndNotation } from "../util/editor"; import { @@ -55,19 +58,11 @@ const handleEditMenu = wrappedAction(({ key }) => { ]; break; case "add-paragraph": { - store.paragraphs.push( - createParagraph({ - notations: [ - createNotation({ note: "0" }), - createNotation({ note: "0" }), - createNotation({ note: "0" }), - createNotation({ note: "0" }), - createNotation({ note: N.separator }), - ], - }) - ); + store.paragraphs.push(createParagraphWithNotations()); break; } + default: + break; } }); const handleConvertMenu = wrappedAction(() => { @@ -176,8 +171,19 @@ const handleKeyPress = wrappedAction((ev) => { } case inputKey === "enter" && ctrl && !shift: { const newNotation = createNotation(); - paragraph.notations.splice(notationIndex + 1, 0, newNotation); - state.selectedNotationKey = newNotation.key; + if ( + notationIndex === paragraph.notations.length - 1 && + paragraphIndex === store.paragraphs.length - 1 + ) { + // 在最后一个音符时ctrl+enter插入新段落并创建符号 + store.paragraphs.push(createParagraph({ notations: [newNotation] })); + state.selectedNotationKey = store.paragraphs + .at(-1) + .notations.at(0).key; + } else { + paragraph.notations.splice(notationIndex + 1, 0, newNotation); + state.selectedNotationKey = newNotation.key; + } break; } case inputKey === "j" && !ctrl && !shift: { @@ -218,6 +224,8 @@ const handleKeyPress = wrappedAction((ev) => { paragraph.alignJustify = !paragraph.alignJustify; break; } + default: + break; } } else { // 仅未选中符号时 @@ -226,7 +234,10 @@ const handleKeyPress = wrappedAction((ev) => { state.selectedNotationKey = state.lastSelectedNotationKey || store.paragraphs?.at(0)?.notations?.at(0).key; + break; } + default: + break; } } // 全局 @@ -261,6 +272,8 @@ const handleKeyPress = wrappedAction((ev) => { } break; } + default: + break; } }); diff --git a/src/menu/notation.js b/src/menu/notation.js index 2850e5b..dfdf795 100644 --- a/src/menu/notation.js +++ b/src/menu/notation.js @@ -1,25 +1,19 @@ import { - ArrowDownOutlined, - ArrowUpOutlined, DeleteOutlined, - FontColorsOutlined, - MinusOutlined, - PauseOutlined, - PlusOutlined, RadiusSettingOutlined, StopOutlined, } from "@ant-design/icons"; -import { Menu, message } from "antd"; +import { Menu } from "antd"; import state from "../store/state"; import { findParagraphAndNotation } from "../util/editor"; -import { isNote, isSeparator } from "../util/notation"; +import { isNote } from "../util/notation"; import { runInWrappedAction, wrappedAction } from "../store/history"; const getNotationContextMenu = (notation, paragraph) => { const hasTie = notation.tieTo; const handleMenu = wrappedAction(function ({ key }) { switch (true) { - case key === "tie": + case key === "tie": { if (!hasTie) { state.tieSourceKey = notation.key; return; @@ -30,6 +24,7 @@ const getNotationContextMenu = (notation, paragraph) => { } notation.tieTo = null; break; + } case key === "break-underline": notation.breakUnderline = !notation.breakUnderline; break; @@ -38,76 +33,35 @@ const getNotationContextMenu = (notation, paragraph) => { notation.note = separator; break; } + default: + break; } }); return ( // ENHANCE: 分音符类型显示菜单项 - { + {isNote(notation) && ( }> {notation.breakUnderline ? "在此处延续增减时线" : "在此处打断增减时线"} - } - { - }> + )} + {isNote(notation) && ( + } + notation={notation} + > {notation.tieTo ? "删除连音线" : "从此处添加连音线到..."} - } + )} }> 删除 ); - - return [ - { - key: "octaveUp", - text: "升高一个八度(8)", - visible: isNote(notation), - onClick: wrappedAction(() => { - notation.octave += 1; - }), - }, - { - key: "octaveDown", - text: "降低一个八度(Shift + 8)", - visible: isNote(notation), - onClick: wrappedAction(() => { - notation.octave -= 1; - }), - }, - { - key: "tie", - text: notation.tieTo ? "删除连音线" : "从此处添加连音线到...", - visible: isNote(notation), - onClick: wrappedAction(() => {}), - }, - { - key: "break-underline", - text: notation.breakUnderline - ? "在此处延续增减时线" - : "在此处打断增减时线", - visible: isNote(notation), - onClick: wrappedAction(() => { - notation.breakUnderline = !notation.breakUnderline; - }), - }, - { - key: "delete", - text: "删除", - onClick: wrappedAction(() => { - const index = paragraph.notations.indexOf(notation); - if (index !== -1) { - paragraph.notations.splice(index, 1); - } else { - message.error("符号不存在"); - } - }), - }, - ]; }; // ENHANCE: 使用EditableContent组件的overlay属性传入菜单 diff --git a/src/menu/paragraph.js b/src/menu/paragraph.js index f1edb12..eafe4da 100644 --- a/src/menu/paragraph.js +++ b/src/menu/paragraph.js @@ -32,6 +32,8 @@ function getParagraphMenuOptions(paragraph) { store.paragraphs.splice(idx, 1); } break; + default: + break; } }); diff --git a/src/store/history.js b/src/store/history.js index 419a4ec..5e2eb8b 100644 --- a/src/store/history.js +++ b/src/store/history.js @@ -1,4 +1,4 @@ -import { action, intercept, keys, runInAction, toJS } from "mobx"; +import { action, runInAction, toJS } from "mobx"; import globalStore from "./global"; const storeHistory = []; @@ -43,12 +43,10 @@ function go(span) { Math.max(0, cursor + span), storeHistory.length - 1 ); - console.log("go ", span); if (newCursor === cursor) { return; } cursor = newCursor; - console.log("gone to ", newCursor); Object.assign(globalStore, storeHistory[newCursor]); } @@ -64,11 +62,6 @@ function executeAction(actionFunc, ...args) { if (!_isEqual(storeHistory[cursor], current)) { storeHistory[++cursor] = current; storeHistory.length = cursor + 1; - console.log( - "pushed history, length & cursor: ", - storeHistory.length, - cursor - ); if (storeHistory.length > 256) { storeHistory.splice(0, 256 - storeHistory.length); } diff --git a/src/util/editor.js b/src/util/editor.js index 361170c..65f8f88 100644 --- a/src/util/editor.js +++ b/src/util/editor.js @@ -1,6 +1,5 @@ import store from "../store/global"; -import { createNotation, notations } from "../util/notation"; -import { createParagraph } from "../util/paragraph"; +import { createParagraphWithNotations } from "../util/paragraph"; import { VERSION } from "./version"; const initialData = { @@ -16,7 +15,7 @@ const initialData = { gapAfterTitle: 16, gapAfterHeader: 32, gapBetweenParagraph: 32, - gapBetweenNotation: 8, + gapBetweenNotation: 16, beat: [4, 4], speed: 75, authors: ["佚名 作词", "简谱编辑器 制谱"], @@ -28,17 +27,7 @@ function getDefaultGlobalData() { } function getDefaultGlobalDataWidthNotations() { const d = getDefaultGlobalData(); - d.paragraphs = [ - createParagraph({ - notations: [ - createNotation({ note: "0" }), - createNotation({ note: "0" }), - createNotation({ note: "0" }), - createNotation({ note: "0" }), - createNotation({ note: notations.separator }), - ], - }), - ]; + d.paragraphs = [createParagraphWithNotations()]; return d; } @@ -55,6 +44,7 @@ function findParagraphAndNotation(key) { res.paragraphIndex = i; return true; } + return false; }); return res; } diff --git a/src/util/file.js b/src/util/file.js index 0ca1ce0..551306b 100644 --- a/src/util/file.js +++ b/src/util/file.js @@ -1,6 +1,7 @@ import { message } from "antd"; import { remove, transaction } from "mobx"; import { toJS } from "mobx"; +import globalState from "../store/state"; import store from "../store/global"; import { clearHistory } from "../store/history"; import domtoimage from "./dom-to-image"; @@ -20,9 +21,9 @@ async function loadFile(file) { const data = await read(file); if (!(data.version <= VERSION)) { message.error("不支持的文件格式,请使用最新版本"); - console.log(data); return; } + globalState.lastSelectedNotationKey = globalState.selectedNotationKey = null; clearHistory(); transaction(() => { for (const key of Object.keys(store)) { @@ -44,12 +45,6 @@ function saveFile() { } function exportFile() { - // const canvas = document.createElement("canvas"); - // const ctx = canvas.getContext("2d"); - // const dpi = window.devicePixelRatio || 1; - // canvas.style.width = store.canvasWidth; - // canvas.style.height = store.canvasHeight; - // ctx.scale(dpi, dpi); domtoimage.toPng(document.querySelector("#temp_svg")).then((dataUrl) => { const downloadLink = document.createElement("a"); downloadLink.download = (store.title || "未标题") + ".png"; diff --git a/src/util/paragraph.js b/src/util/paragraph.js index 04cf667..ef6b2d2 100644 --- a/src/util/paragraph.js +++ b/src/util/paragraph.js @@ -1,3 +1,5 @@ +import { createNotation, notations as N } from "./notation"; + // 新建段落 function createParagraph(initial) { const p = { @@ -12,5 +14,16 @@ function createParagraph(initial) { } return p; } +function createParagraphWithNotations() { + return createParagraph({ + notations: [ + createNotation({ note: "0" }), + createNotation({ note: "0" }), + createNotation({ note: "0" }), + createNotation({ note: "0" }), + createNotation({ note: N.separator }), + ], + }); +} -export { createParagraph }; +export { createParagraph, createParagraphWithNotations }; diff --git a/src/util/placement.js b/src/util/placement.js index ce453b9..f96707e 100644 --- a/src/util/placement.js +++ b/src/util/placement.js @@ -6,22 +6,6 @@ import store from "../store/global"; let canvas, context2D, defaultFontFamily; let measureSvgEl, measureTextEl; -function _calcFontData(fontSize) { - const span = document.createElement("span"); - span.style.fontFamily = window.getComputedStyle(document.body).fontFamily; - span.style.fontSize = `${fontSize}px`; - span.style.visibility = "hidden"; - span.style.position = "absolute"; - span.style.display = "inline-block"; - span.textContent = "x"; - document.body.appendChild(span); - const data = span.getBoundingClientRect(); - return { - xWidth: data.width, - xHeight: data.height, - }; -} - function _initializeCanvas(fontSize) { if (!canvas) { canvas = document.createElement("canvas"); @@ -151,7 +135,8 @@ function calcParagraphContentHeight(paragraph) { } let tieHeight = 0; if (_hasTie(paragraph)) { - tieHeight = placement.maxTieHeight; + // FIXME: 应该计算连音线高度 + // tieHeight = placement.maxTieHeight; } // 段落高度不是最高的音符的高度,而是上边偏移最大的音符的上部分偏移量+下边偏移最 // 大的音符的下部分偏移量 @@ -184,7 +169,8 @@ function calcParagraphAboveOffset(paragraph) { } let tieHeight = 0; if (_hasTie(paragraph)) { - tieHeight = placement.maxTieHeight; + // FIXME: 应该计算连音线高度 + // tieHeight = placement.maxTieHeight; } const notationOffset = notations .map((n) => calcNotationAboveOffset(n)) diff --git a/src/view/ConfigModal/index.js b/src/view/ConfigModal/index.js index 40baa09..a5c0bd9 100644 --- a/src/view/ConfigModal/index.js +++ b/src/view/ConfigModal/index.js @@ -1,4 +1,4 @@ -import { Button, Form, Input, InputNumber, Modal } from "antd"; +import { Button, Form, Input, Modal } from "antd"; import { observer } from "mobx-react-lite"; import { useCallback } from "react"; import store from "../../store/global"; @@ -26,25 +26,28 @@ function ConfigModal({ visible, onVisibleChange }) { const handleClose = useCallback(() => { onVisibleChange?.call(null, false); }, [onVisibleChange]); - const resetConfig = useCallback( - unwrappedAction(() => { - const { - canvasWidth, - canvasHeight, - marginTop, - marginHorizontal, - } = getDefaultGlobalData(); - Object.assign(store, { - canvasWidth, - canvasHeight, - marginTop, - marginHorizontal, - }); - }) - ); + const resetConfig = unwrappedAction(() => { + const { + canvasWidth, + canvasHeight, + marginTop, + marginHorizontal, + gapBetweenParagraph, + gapBetweenNotation, + } = getDefaultGlobalData(); + Object.assign(store, { + canvasWidth, + canvasHeight, + marginTop, + marginHorizontal, + gapBetweenParagraph, + gapBetweenNotation, + }); + }); return ( + + ); diff --git a/src/view/Editor/index.js b/src/view/Editor/index.js index 135b851..7232c5f 100644 --- a/src/view/Editor/index.js +++ b/src/view/Editor/index.js @@ -7,7 +7,7 @@ import { SettingOutlined, } from "@ant-design/icons"; import { observer } from "mobx-react-lite"; -import { useCallback, useEffect, useState } from "react"; +import { useEffect } from "react"; import state from "../../store/state"; import store from "../../store/global"; import P, { @@ -31,7 +31,6 @@ import Row from "../Row"; import Styles from "./index.module.css"; function Editor() { - const [isHelpModalVisible, setIsHelpModalVisible] = useState(false); const heightCache = []; function accumulate(index) { const paragraphs = store.paragraphs || []; @@ -45,23 +44,18 @@ function Editor() { height += calcParagraphAboveOffset(paragraphs[index]); return height; } - const handleChangeConfigModalVisibility = useCallback( - unwrappedAction((visible) => { - state.configDialogVisible = visible; - }, []) - ); - const handleChangeHelpModalVisibility = useCallback( - unwrappedAction((visible) => { - state.helpDialogVisible = visible; - }, []) - ); - const handleShowConfigDialog = useCallback(() => { + const handleChangeConfigModalVisibility = unwrappedAction((visible) => { + state.configDialogVisible = visible; + }); + const handleChangeHelpModalVisibility = unwrappedAction((visible) => { + state.helpDialogVisible = visible; + }); + const handleShowConfigDialog = () => { handleChangeConfigModalVisibility(true); - }, []); - const handleShowHelpDialog = useCallback(() => { + }; + const handleShowHelpDialog = () => { handleChangeHelpModalVisibility(true); - }, []); - + }; useEffect(() => { document.addEventListener("keydown", handleKeyPress); return () => { diff --git a/src/view/Header/LeftInfoBlock/index.js b/src/view/Header/LeftInfoBlock/index.js index 9871adf..dd5d04e 100644 --- a/src/view/Header/LeftInfoBlock/index.js +++ b/src/view/Header/LeftInfoBlock/index.js @@ -1,4 +1,3 @@ -import { action } from "mobx"; import { message } from "antd"; import { observer } from "mobx-react-lite"; import EditableContent from "../../../component/EditableContent"; diff --git a/src/view/Header/RightInfoBlock/index.js b/src/view/Header/RightInfoBlock/index.js index c04ee14..e765e7c 100644 --- a/src/view/Header/RightInfoBlock/index.js +++ b/src/view/Header/RightInfoBlock/index.js @@ -1,6 +1,5 @@ import { DeleteOutlined, PlusOutlined } from "@ant-design/icons"; import { Modal } from "antd"; -import { action } from "mobx"; import { observer } from "mobx-react-lite"; import EditableContent from "../../../component/EditableContent"; import P from "../../../util/placement"; diff --git a/src/view/Header/Title/index.js b/src/view/Header/Title/index.js index dc5bd11..4d81170 100644 --- a/src/view/Header/Title/index.js +++ b/src/view/Header/Title/index.js @@ -1,6 +1,5 @@ -import { action } from "mobx"; import { observer } from "mobx-react-lite"; -import { useEffect, useRef } from "react"; +import { useRef } from "react"; import EditableContent from "../../../component/EditableContent"; import store from "../../../store/global"; import { wrappedAction } from "../../../store/history"; diff --git a/src/view/HelpModal/index.js b/src/view/HelpModal/index.js index 8a65a89..be440d1 100644 --- a/src/view/HelpModal/index.js +++ b/src/view/HelpModal/index.js @@ -1,7 +1,5 @@ import { Button, Modal } from "antd"; import { useCallback } from "react"; -import state from "../../store/state"; -import { unwrappedAction } from "../../store/history"; import Styles from "./index.module.css"; function KeyboardDescItem({ kbd, desc }) { diff --git a/src/view/Notation/index.js b/src/view/Notation/index.js index de9d972..83624da 100644 --- a/src/view/Notation/index.js +++ b/src/view/Notation/index.js @@ -1,4 +1,3 @@ -import { action } from "mobx"; import { message } from "antd"; import { observer } from "mobx-react-lite"; import EditableContent from "../../component/EditableContent"; @@ -7,7 +6,7 @@ import store from "../../store/global"; import P, { calcSubTextWidth } from "../../util/placement"; import { findParagraphAndNotation } from "../../util/editor"; import { getNotationContextMenu } from "../../menu/notation"; -import { notations } from "../../util/notation"; +import { isNote, notations } from "../../util/notation"; import { wrappedAction } from "../../store/history"; import Row from "../Row"; import Text from "../Text"; @@ -40,11 +39,13 @@ function Notation({ offsetX, notation, paragraph }) { state.tieSourceKey = null; return; } - if (sourceParagraph.notations.includes(notation)) { + if (!sourceParagraph.notations.includes(notation)) { + message.warn("只允许相同段落的音符相连!"); + } else if (!isNote(notation)) { + message.warn("只允许在音符上添加连音线!"); + } else { sourceNotation.tieTo = notation.key; notation.tieTo = sourceNotation.key; - } else { - message.warn("只允许相同段落的音符相连!"); } state.tieSourceKey = null; } diff --git a/src/view/Paragraph/index.js b/src/view/Paragraph/index.js index 6bfa615..72e272b 100644 --- a/src/view/Paragraph/index.js +++ b/src/view/Paragraph/index.js @@ -8,15 +8,11 @@ import P, { calcParagraphContentHeight, calcParagraphWidth, } from "../../util/placement"; -import { - getParagraphMenuOptions, - handleSelectParagraphMenu, -} from "../../menu/paragraph"; +import { getParagraphMenuOptions } from "../../menu/paragraph"; import Notation from "../Notation"; import Row from "../Row"; function Paragraph({ paragraph, offsetY, alignJustify }) { - console.log("render paragraph"); const notations = paragraph.notations || []; const widthCache = []; let itemFlexOffset = 0;