feat: use embedded inputbox for track name editing and region name editing

This commit is contained in:
Xiaohan-Tian
2026-05-24 23:33:54 -07:00
parent 27948771de
commit b80066c2a4
5 changed files with 204 additions and 53 deletions
+22 -1
View File
@@ -316,11 +316,32 @@
font-weight: bold;
color: #e0e0e0;
/* text-transform: uppercase; */
cursor: pointer;
cursor: text;
padding: 5px;
border-radius: 3px;
}
.piano-roll-title-input {
flex: 1;
min-width: 0;
margin: 0;
padding: 5px 8px;
box-sizing: border-box;
border: 1px solid #5a5a5a;
border-radius: 6px;
outline: none;
background-color: #4a4a4a;
color: #e0e0e0;
font-size: 12px;
font-weight: bold;
text-align: center;
}
.piano-roll-title-input:focus {
border-color: #5a9fd4;
background-color: #4a4a4a;
}
.close-button {
background: transparent;
border: none;
+48 -18
View File
@@ -17,7 +17,7 @@ import { ConfigManager } from '../../core/config/ConfigManager';
import { beatsToBar } from '../../util/midiUtil';
import { UpdateRegionCommand } from '../../core/commands';
import { getSuitableChords, noteNameToPitchClass } from '../../util/scaleUtil';
import { showAlert, showPrompt } from '../../util/dialogUtil';
import { showAlert } from '../../util/dialogUtil';
import {
normalizeSpectrogramHeightResolution,
type SpectrogramHeightResolution,
@@ -103,6 +103,9 @@ const PianoRoll: React.FC<PianoRollProps> = ({
const [sheetMusicTrackScopeEnabled, setSheetMusicTrackScopeEnabled] = useState(false);
const [sheetQuantization, setSheetQuantization] = useState('16,48');
const [sheetMeasureMetrics, setSheetMeasureMetrics] = useState<SheetMeasureMetric[]>([]);
const [isEditingTitle, setIsEditingTitle] = useState(false);
const [titleInputValue, setTitleInputValue] = useState('');
const titleInputRef = useRef<HTMLInputElement>(null);
// Quantization state
const [quantPosition, setQuantPosition] = useState<string>('1/8');
@@ -419,43 +422,64 @@ const PianoRoll: React.FC<PianoRollProps> = ({
};
}, [isDragging, isResizing, dragOffset, position]);
// Handle title click to rename the region
const handleTitleClick = async () => {
// If we were just dragging, don't show the rename dialog
if (wasDraggingRef.current) {
if (DEBUG_MODE.PIANO_ROLL) {
console.log("Skipping rename dialog because the window was just dragged");
}
useEffect(() => {
if (!isEditingTitle && activeRegion) {
setTitleInputValue(activeRegion.getName());
}
}, [activeRegion, isEditingTitle]);
const cancelTitleEdit = () => {
setTitleInputValue(activeRegion?.getName() ?? '');
setIsEditingTitle(false);
};
const commitTitleEdit = async () => {
if (!activeRegion) {
setIsEditingTitle(false);
return;
}
if (!activeRegion) return;
const newName = titleInputValue.trim();
setIsEditingTitle(false);
setTitleInputValue(activeRegion.getName());
// Show a prompt to get the new name
const newName = await showPrompt("Enter a new name for the region:", activeRegion.getName());
if (!newName || newName === activeRegion.getName()) return;
// If the user clicked Cancel or entered an empty string, do nothing
if (!newName || newName.trim() === '' || newName === activeRegion.getName()) return;
// Use command pattern to update the region name with undo support
try {
const command = new UpdateRegionCommand(activeRegion.getId(), { name: newName.trim() });
const command = new UpdateRegionCommand(activeRegion.getId(), { name: newName });
KGCore.instance().executeCommand(command);
if (DEBUG_MODE.PIANO_ROLL) {
console.log(`Executed UpdateRegionCommand: renamed region ${activeRegion.getId()} to "${newName}" using command pattern`);
}
// Update the store to trigger re-render
const updatedTracks = [...tracks];
useProjectStore.setState({ tracks: updatedTracks });
} catch (error) {
console.error('Error renaming region:', error);
await showAlert('Failed to rename region. Please try again.');
}
};
// Handle title click to rename the region
const handleTitleClick = () => {
// If we were just dragging, don't show the rename dialog
if (wasDraggingRef.current) {
if (DEBUG_MODE.PIANO_ROLL) {
console.log("Skipping inline rename because the window was just dragged");
}
return;
}
if (!activeRegion) return;
setTitleInputValue(activeRegion.getName());
setIsEditingTitle(true);
window.setTimeout(() => {
titleInputRef.current?.focus();
titleInputRef.current?.select();
}, 0);
};
// Handle tool selection
const handleToolSelect = (tool: 'pointer' | 'pencil') => {
setActiveTool(tool);
@@ -1274,8 +1298,14 @@ const PianoRoll: React.FC<PianoRollProps> = ({
<PianoRollHeader
onClose={onClose}
title={getPianoRollTitle()}
isEditingTitle={isEditingTitle}
titleInputValue={titleInputValue}
onTitleClick={handleTitleClick}
onTitleInputChange={setTitleInputValue}
onTitleCommit={() => { void commitTitleEdit(); }}
onTitleCancel={cancelTitleEdit}
onMouseDown={(e) => handleMouseDown(e, 'drag')}
titleInputRef={titleInputRef}
/>
<PianoRollToolbar
+46 -9
View File
@@ -4,28 +4,65 @@ import { FaTimes } from 'react-icons/fa';
interface PianoRollHeaderProps {
onClose: () => void;
title: string;
isEditingTitle: boolean;
titleInputValue: string;
onTitleClick: () => void;
onTitleInputChange: (value: string) => void;
onTitleCommit: () => void;
onTitleCancel: () => void;
onMouseDown: (e: React.MouseEvent) => void;
titleInputRef: React.RefObject<HTMLInputElement | null>;
}
const PianoRollHeader: React.FC<PianoRollHeaderProps> = ({
onClose,
title,
isEditingTitle,
titleInputValue,
onTitleClick,
onMouseDown
onTitleInputChange,
onTitleCommit,
onTitleCancel,
onMouseDown,
titleInputRef
}) => {
return (
<div
className="piano-roll-header"
onMouseDown={onMouseDown}
>
<div
className="piano-roll-title"
onClick={onTitleClick}
title="Click to rename region"
>
{title}
</div>
{isEditingTitle ? (
<input
ref={titleInputRef}
className="piano-roll-title-input"
type="text"
value={titleInputValue}
onChange={(e) => onTitleInputChange(e.target.value.replace(/\r?\n/g, ' '))}
onBlur={onTitleCommit}
onClick={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
e.stopPropagation();
onTitleCommit();
} else if (e.key === 'Escape') {
e.preventDefault();
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
onTitleCancel();
}
}}
/>
) : (
<div
className="piano-roll-title"
onClick={onTitleClick}
title="Click to rename region"
>
{title}
</div>
)}
<button
className="close-button"
onClick={onClose}
@@ -36,4 +73,4 @@ const PianoRollHeader: React.FC<PianoRollHeaderProps> = ({
);
};
export default PianoRollHeader;
export default PianoRollHeader;