mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-02 06:58:11 +08:00
Add support for double-clicking to rename document tabs (#4072)
This commit is contained in:
@@ -83,6 +83,7 @@
|
|||||||
data-tooltip-description={tooltipDescription}
|
data-tooltip-description={tooltipDescription}
|
||||||
data-tooltip-shortcut={tooltipShortcut?.shortcut ? JSON.stringify(tooltipShortcut.shortcut) : undefined}
|
data-tooltip-shortcut={tooltipShortcut?.shortcut ? JSON.stringify(tooltipShortcut.shortcut) : undefined}
|
||||||
for={forCheckbox !== undefined ? `checkbox-input-${forCheckbox}` : undefined}
|
for={forCheckbox !== undefined ? `checkbox-input-${forCheckbox}` : undefined}
|
||||||
|
on:dblclick
|
||||||
bind:this={self}
|
bind:this={self}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
export let clickAction: ((index: number) => void) | undefined = undefined;
|
export let clickAction: ((index: number) => void) | undefined = undefined;
|
||||||
export let closeAction: ((index: number) => void) | undefined = undefined;
|
export let closeAction: ((index: number) => void) | undefined = undefined;
|
||||||
export let reorderAction: ((oldIndex: number, newIndex: number) => void) | undefined = undefined;
|
export let reorderAction: ((oldIndex: number, newIndex: number) => void) | undefined = undefined;
|
||||||
|
export let renameAction: ((index: number, newName: string) => void) | undefined = undefined;
|
||||||
export let emptySpaceAction: (() => void) | undefined = undefined;
|
export let emptySpaceAction: (() => void) | undefined = undefined;
|
||||||
export let crossPanelDropAction: ((sourcePanelId: string, targetPanelId: string, insertIndex: number) => void) | undefined = undefined;
|
export let crossPanelDropAction: ((sourcePanelId: string, targetPanelId: string, insertIndex: number) => void) | undefined = undefined;
|
||||||
export let groupDropAction: ((sourcePanelId: string, targetPanelId: string, insertIndex: number) => void) | undefined = undefined;
|
export let groupDropAction: ((sourcePanelId: string, targetPanelId: string, insertIndex: number) => void) | undefined = undefined;
|
||||||
@@ -50,6 +51,28 @@
|
|||||||
|
|
||||||
let tabElements: (LayoutRow | undefined)[] = [];
|
let tabElements: (LayoutRow | undefined)[] = [];
|
||||||
|
|
||||||
|
// Document tab rename state
|
||||||
|
let editingNameTabIndex: number | undefined = undefined;
|
||||||
|
let editingNameText = "";
|
||||||
|
let editingNameInputElement: HTMLInputElement | undefined = undefined;
|
||||||
|
|
||||||
|
async function setEditingTabName(tabIndex: number, currentName: string) {
|
||||||
|
editingNameText = currentName;
|
||||||
|
editingNameTabIndex = tabIndex;
|
||||||
|
|
||||||
|
await tick();
|
||||||
|
|
||||||
|
editingNameInputElement?.focus();
|
||||||
|
editingNameInputElement?.select();
|
||||||
|
}
|
||||||
|
|
||||||
|
function commitEditingTabName(event: Event) {
|
||||||
|
if (editingNameTabIndex === undefined || !(event.target instanceof HTMLInputElement)) return;
|
||||||
|
|
||||||
|
renameAction?.(editingNameTabIndex, event.target.value);
|
||||||
|
editingNameTabIndex = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
// Tab drag-and-drop state
|
// Tab drag-and-drop state
|
||||||
let dragStartState: { tabIndex: number; pointerX: number; pointerY: number; isGroupDrag: boolean } | undefined = undefined;
|
let dragStartState: { tabIndex: number; pointerX: number; pointerY: number; isGroupDrag: boolean } | undefined = undefined;
|
||||||
let dragging = false;
|
let dragging = false;
|
||||||
@@ -392,9 +415,30 @@
|
|||||||
bind:this={tabElements[tabIndex]}
|
bind:this={tabElements[tabIndex]}
|
||||||
>
|
>
|
||||||
<LayoutRow class="name">
|
<LayoutRow class="name">
|
||||||
<TextLabel class="text">{tabLabel.name}</TextLabel>
|
{#if editingNameTabIndex !== tabIndex}
|
||||||
|
<TextLabel class="text" on:dblclick={() => renameAction && setEditingTabName(tabIndex, tabLabel.name)}>{tabLabel.name}</TextLabel>
|
||||||
|
{:else}
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
bind:this={editingNameInputElement}
|
||||||
|
bind:value={editingNameText}
|
||||||
|
on:pointerdown|stopPropagation
|
||||||
|
on:dblclick|stopPropagation
|
||||||
|
on:blur={commitEditingTabName}
|
||||||
|
on:keydown={(e) => {
|
||||||
|
// Stop propagation when we handle the key ourselves so the global keyboard forwarder doesn't dispatch them and trigger unrelated bindings
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
commitEditingTabName(e);
|
||||||
|
e.stopPropagation();
|
||||||
|
} else if (e.key === "Escape") {
|
||||||
|
editingNameTabIndex = undefined;
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
{#if tabLabel.unsaved}
|
{#if tabLabel.unsaved}
|
||||||
<TextLabel>*</TextLabel>
|
<TextLabel classes={{ hidden: editingNameTabIndex === tabIndex }}>*</TextLabel>
|
||||||
{/if}
|
{/if}
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
{#if tabCloseButtons}
|
{#if tabCloseButtons}
|
||||||
@@ -513,11 +557,31 @@
|
|||||||
|
|
||||||
&.text {
|
&.text {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
white-space: nowrap;
|
white-space: pre;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
flex-shrink: 1;
|
flex-shrink: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
color: inherit;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
margin: 0 -4px;
|
||||||
|
padding: 0 4px;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: var(--color-1-nearblack);
|
||||||
|
field-sizing: content;
|
||||||
|
align-self: center;
|
||||||
|
// Stack above the absolutely-positioned close button so it doesn't intercept clicks at the input's right edge.
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-label.hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-button {
|
.icon-button {
|
||||||
|
|||||||
@@ -180,6 +180,11 @@
|
|||||||
clickAction={(tabIndex) => editor.selectDocument($portfolio.documents[tabIndex].id)}
|
clickAction={(tabIndex) => editor.selectDocument($portfolio.documents[tabIndex].id)}
|
||||||
closeAction={(tabIndex) => editor.closeDocumentWithConfirmation($portfolio.documents[tabIndex].id)}
|
closeAction={(tabIndex) => editor.closeDocumentWithConfirmation($portfolio.documents[tabIndex].id)}
|
||||||
reorderAction={(oldIndex, newIndex) => editor.reorderDocument($portfolio.documents[oldIndex].id, newIndex)}
|
reorderAction={(oldIndex, newIndex) => editor.reorderDocument($portfolio.documents[oldIndex].id, newIndex)}
|
||||||
|
renameAction={(tabIndex, newName) => {
|
||||||
|
// Ensure the target document is the active one before renaming, since `RenameDocument` operates on the active document
|
||||||
|
editor.selectDocument($portfolio.documents[tabIndex].id);
|
||||||
|
editor.renameDocument(newName);
|
||||||
|
}}
|
||||||
tabActiveIndex={$portfolio.activeDocumentIndex}
|
tabActiveIndex={$portfolio.activeDocumentIndex}
|
||||||
groupDropAction={groupDrop}
|
groupDropAction={groupDrop}
|
||||||
splitDropAction={splitDrop}
|
splitDropAction={splitDrop}
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ impl EditorWrapper {
|
|||||||
pub fn load_document_content(&self, document_id: u64, document: String) {
|
pub fn load_document_content(&self, document_id: u64, document: String) {
|
||||||
let message = PersistentStateMessage::LoadDocument {
|
let message = PersistentStateMessage::LoadDocument {
|
||||||
document_id: DocumentId(document_id),
|
document_id: DocumentId(document_id),
|
||||||
document: document,
|
document,
|
||||||
};
|
};
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
@@ -407,6 +407,13 @@ impl EditorWrapper {
|
|||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rename the currently active document.
|
||||||
|
#[wasm_bindgen(js_name = renameDocument)]
|
||||||
|
pub fn rename_document(&self, new_name: String) {
|
||||||
|
let message = DocumentMessage::RenameDocument { new_name };
|
||||||
|
self.dispatch(message);
|
||||||
|
}
|
||||||
|
|
||||||
#[wasm_bindgen(js_name = newDocumentDialog)]
|
#[wasm_bindgen(js_name = newDocumentDialog)]
|
||||||
pub fn new_document_dialog(&self) {
|
pub fn new_document_dialog(&self) {
|
||||||
let message = DialogMessage::RequestNewDocumentDialog;
|
let message = DialogMessage::RequestNewDocumentDialog;
|
||||||
|
|||||||
Reference in New Issue
Block a user