Update gallery_app.py
This commit is contained in:
@@ -647,28 +647,29 @@ def refresh_ui():
|
|||||||
render_gallery()
|
render_gallery()
|
||||||
|
|
||||||
def handle_keyboard(e):
|
def handle_keyboard(e):
|
||||||
"""Handle keyboard navigation and shortcuts."""
|
"""Handle keyboard navigation and shortcuts (fallback)."""
|
||||||
if not e.action.keydown:
|
if not e.action.keydown:
|
||||||
return
|
return
|
||||||
|
|
||||||
key = e.key
|
key = e.key.name if hasattr(e.key, 'name') else str(e.key)
|
||||||
|
ctrl = e.modifiers.ctrl if hasattr(e.modifiers, 'ctrl') else False
|
||||||
|
|
||||||
# Navigation
|
# Navigation - arrow keys
|
||||||
if key.arrow_left and state.page > 0:
|
if key == 'ArrowLeft' and state.page > 0:
|
||||||
set_page(state.page - 1)
|
set_page(state.page - 1)
|
||||||
elif key.arrow_right and state.page < state.total_pages - 1:
|
elif key == 'ArrowRight' and state.page < state.total_pages - 1:
|
||||||
set_page(state.page + 1)
|
set_page(state.page + 1)
|
||||||
|
|
||||||
# Undo (Ctrl+Z)
|
# Undo (Ctrl+Z)
|
||||||
elif key == 'z' and e.modifiers.ctrl:
|
elif key.lower() == 'z' and ctrl:
|
||||||
action_undo()
|
action_undo()
|
||||||
|
|
||||||
# Save (Ctrl+S)
|
# Save (Ctrl+S)
|
||||||
elif key == 's' and e.modifiers.ctrl:
|
elif key.lower() == 's' and ctrl:
|
||||||
action_save_tags()
|
action_save_tags()
|
||||||
|
|
||||||
# Quick category switch (Ctrl+1 through Ctrl+5)
|
# Quick category switch (Ctrl+1 through Ctrl+5)
|
||||||
elif e.modifiers.ctrl and key in '12345':
|
elif ctrl and key in '12345':
|
||||||
cats = state.get_categories()
|
cats = state.get_categories()
|
||||||
cat_idx = int(key) - 1
|
cat_idx = int(key) - 1
|
||||||
if cat_idx < len(cats):
|
if cat_idx < len(cats):
|
||||||
@@ -678,20 +679,20 @@ def handle_keyboard(e):
|
|||||||
ui.notify(f"Category: {state.active_cat}", type='info')
|
ui.notify(f"Category: {state.active_cat}", type='info')
|
||||||
|
|
||||||
# Number keys 1-9 to tag hovered image
|
# Number keys 1-9 to tag hovered image
|
||||||
elif key in '123456789' and not e.modifiers.ctrl:
|
elif key in '123456789' and not ctrl:
|
||||||
if state.hovered_image and state.hovered_image not in state.staged_data:
|
if state.hovered_image and state.hovered_image not in state.staged_data:
|
||||||
action_tag(state.hovered_image, int(key))
|
action_tag(state.hovered_image, int(key))
|
||||||
|
|
||||||
# 0 key to tag with next_index
|
# 0 key to tag with next_index
|
||||||
elif key == '0' and state.hovered_image and state.hovered_image not in state.staged_data:
|
elif key == '0' and not ctrl and state.hovered_image and state.hovered_image not in state.staged_data:
|
||||||
action_tag(state.hovered_image)
|
action_tag(state.hovered_image)
|
||||||
|
|
||||||
# U to untag hovered image
|
# U to untag hovered image
|
||||||
elif key == 'u' and state.hovered_image and state.hovered_image in state.staged_data:
|
elif key.lower() == 'u' and not ctrl and state.hovered_image and state.hovered_image in state.staged_data:
|
||||||
action_untag(state.hovered_image)
|
action_untag(state.hovered_image)
|
||||||
|
|
||||||
# F to cycle filter modes
|
# F to cycle filter modes
|
||||||
elif key == 'f' and not e.modifiers.ctrl:
|
elif key.lower() == 'f' and not ctrl:
|
||||||
modes = ["all", "untagged", "tagged"]
|
modes = ["all", "untagged", "tagged"]
|
||||||
current_idx = modes.index(state.filter_mode)
|
current_idx = modes.index(state.filter_mode)
|
||||||
state.filter_mode = modes[(current_idx + 1) % 3]
|
state.filter_mode = modes[(current_idx + 1) % 3]
|
||||||
@@ -699,6 +700,49 @@ def handle_keyboard(e):
|
|||||||
refresh_ui()
|
refresh_ui()
|
||||||
ui.notify(f"Filter: {state.filter_mode}", type='info')
|
ui.notify(f"Filter: {state.filter_mode}", type='info')
|
||||||
|
|
||||||
|
def process_key(key: str, ctrl: bool):
|
||||||
|
"""Process keyboard input from JS event."""
|
||||||
|
# Navigation
|
||||||
|
if key == 'arrowleft' and state.page > 0:
|
||||||
|
set_page(state.page - 1)
|
||||||
|
elif key == 'arrowright' and state.page < state.total_pages - 1:
|
||||||
|
set_page(state.page + 1)
|
||||||
|
# Undo
|
||||||
|
elif key == 'z' and ctrl:
|
||||||
|
action_undo()
|
||||||
|
# Save
|
||||||
|
elif key == 's' and ctrl:
|
||||||
|
action_save_tags()
|
||||||
|
# Category switch
|
||||||
|
elif ctrl and key in '12345':
|
||||||
|
cats = state.get_categories()
|
||||||
|
cat_idx = int(key) - 1
|
||||||
|
if cat_idx < len(cats):
|
||||||
|
state.active_cat = cats[cat_idx]
|
||||||
|
refresh_staged_info()
|
||||||
|
refresh_ui()
|
||||||
|
ui.notify(f"Category: {state.active_cat}", type='info')
|
||||||
|
# Tag with number
|
||||||
|
elif key in '123456789' and not ctrl:
|
||||||
|
if state.hovered_image and state.hovered_image not in state.staged_data:
|
||||||
|
action_tag(state.hovered_image, int(key))
|
||||||
|
# Tag with next index
|
||||||
|
elif key == '0' and not ctrl:
|
||||||
|
if state.hovered_image and state.hovered_image not in state.staged_data:
|
||||||
|
action_tag(state.hovered_image)
|
||||||
|
# Untag
|
||||||
|
elif key == 'u' and not ctrl:
|
||||||
|
if state.hovered_image and state.hovered_image in state.staged_data:
|
||||||
|
action_untag(state.hovered_image)
|
||||||
|
# Filter
|
||||||
|
elif key == 'f' and not ctrl:
|
||||||
|
modes = ["all", "untagged", "tagged"]
|
||||||
|
current_idx = modes.index(state.filter_mode)
|
||||||
|
state.filter_mode = modes[(current_idx + 1) % 3]
|
||||||
|
state.page = 0
|
||||||
|
refresh_ui()
|
||||||
|
ui.notify(f"Filter: {state.filter_mode}", type='info')
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# MAIN LAYOUT
|
# MAIN LAYOUT
|
||||||
# ==========================================
|
# ==========================================
|
||||||
@@ -719,6 +763,12 @@ def build_header():
|
|||||||
state.load_active_profile()
|
state.load_active_profile()
|
||||||
state.active_cat = "control" # Reset to default category
|
state.active_cat = "control" # Reset to default category
|
||||||
SorterEngine.clear_staging_area() # Clear staging for new profile
|
SorterEngine.clear_staging_area() # Clear staging for new profile
|
||||||
|
|
||||||
|
# Auto-load if source path exists
|
||||||
|
if os.path.exists(state.source_dir):
|
||||||
|
load_images()
|
||||||
|
else:
|
||||||
|
state.all_images = []
|
||||||
refresh_staged_info()
|
refresh_staged_info()
|
||||||
refresh_ui()
|
refresh_ui()
|
||||||
|
|
||||||
@@ -849,7 +899,26 @@ build_header()
|
|||||||
build_sidebar()
|
build_sidebar()
|
||||||
build_main_content()
|
build_main_content()
|
||||||
|
|
||||||
ui.keyboard(on_key=handle_keyboard)
|
# JavaScript keyboard handler for Firefox compatibility
|
||||||
|
ui.add_body_html('''
|
||||||
|
<script>
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
// Skip if typing in input
|
||||||
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
||||||
|
|
||||||
|
const key = e.key.toLowerCase();
|
||||||
|
const ctrl = e.ctrlKey || e.metaKey;
|
||||||
|
|
||||||
|
// Prevent browser defaults for our shortcuts
|
||||||
|
if (ctrl && (key === 's' || key === 'z')) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
''')
|
||||||
|
|
||||||
|
# Use NiceGUI keyboard with active_element_only=False for better capture
|
||||||
|
ui.keyboard(on_key=handle_keyboard, ignore=[], active_element_only=False)
|
||||||
ui.dark_mode().enable()
|
ui.dark_mode().enable()
|
||||||
load_images()
|
load_images()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user