Skip to content
This repository has been archived by the owner on Dec 27, 2024. It is now read-only.

Commit

Permalink
chore: implement keyboard shortcuts
Browse files Browse the repository at this point in the history
  • Loading branch information
sircharlo committed Jun 21, 2024
1 parent 10c8a83 commit 2cc39c1
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 33 deletions.
113 changes: 113 additions & 0 deletions src/components/form-inputs/ShortcutInput.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<template>
<q-btn-group push v-if="localValue">
<q-btn
:color="'blue-' + (8 + index)"
:key="keyboardKey"
:label="keyboardKey"
@click="shortcutPicker = true"
v-for="(keyboardKey, index) in localValue.split('+')"
/>
</q-btn-group>
<q-btn
:label="$t('enter-key-combination')"
@click="shortcutPicker = true"
color="primary"
outline
v-else
/>
<q-dialog
@hide="stopListening()"
@show="startListening()"
persistent
v-model="shortcutPicker"
>
<q-card style="min-width: 350px">
<q-card-section>
<div class="text-h6">Enter a key combination</div>
</q-card-section>

<q-card-section
:class="'q-pt-none ' + (localValue.length > 0 ? 'text-center' : '')"
>
<template v-if="localValue.length > 0">
<q-chip
:key="key"
class="glossy text-center text-uppercase"
color="primary"
size="lg"
square
text-color="white"
v-for="key in localValue.split('+')"
>{{ key }}</q-chip
>
</template>
<p v-else>{{ $t('no-key-combination') }}</p>
</q-card-section>

<q-card-actions align="right" class="text-primary">
<q-btn
:label="$t('clear')"
@click="localValue = ''"
color="negative"
flat
v-close-popup
/>
<q-btn :label="$t('confirm')" flat v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
</template>

<script setup lang="ts">
import {
getCurrentShortcuts,
registerCustomShortcut,
unregisterShortcut,
} from 'src/helpers/keyboardShortcuts';
import { SettingsValues } from 'src/types/settings';
import { ref, watch } from 'vue';
// Define props and emits
const props = defineProps<{
modelValue: string;
shortcutName: keyof SettingsValues;
}>();
const emit = defineEmits(['update:modelValue']);
// Setup component
const localValue = ref(props.modelValue);
watch(localValue, (newValue, oldValue) => {
if (!getCurrentShortcuts().includes(newValue)) {
emit('update:modelValue', newValue);
unregisterShortcut(oldValue);
registerCustomShortcut(props.shortcutName, newValue);
}
});
watch(
() => props.modelValue,
(newValue) => {
localValue.value = newValue;
},
);
const handleKeyPress = (event: KeyboardEvent) => {
const { altKey, ctrlKey, key, metaKey, shiftKey } = event;
const keys = [];
if (ctrlKey) keys.push('Ctrl');
if (shiftKey) keys.push('Shift');
if (altKey) keys.push('Alt');
if (metaKey) keys.push('Meta');
if (key.length < 3 && keys.length > 0) {
keys.push(key);
localValue.value = keys.join('+');
}
};
const startListening = () => window.addEventListener('keydown', handleKeyPress);
const stopListening = () =>
window.removeEventListener('keydown', handleKeyPress);
const shortcutPicker = ref(false);
</script>
31 changes: 29 additions & 2 deletions src/components/media/MusicButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ import { date } from 'quasar';
import { getFileUrl, getPublicationDirectoryContents } from 'src/helpers/fs';
import { formatTime } from 'src/helpers/mediaPlayback';
import { useCurrentStateStore } from 'src/stores/current-state';
import { computed, onMounted, ref, watch } from 'vue';
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
Expand Down Expand Up @@ -127,7 +127,12 @@ const getNextSongUrl = () => {
};
function playMusic() {
if (!musicPlayer.value || !musicPlayerSource.value) return;
if (
!musicPlayer.value ||
!musicPlayerSource.value ||
!currentSettings.value?.enableMusicButton
)
return;
musicPlayer.value.volume =
(getSettingValue('musicVolume') as number) / 100 ?? 1;
musicPlayerSource.value.src = getNextSongUrl();
Expand Down Expand Up @@ -219,4 +224,26 @@ watch(
if (!newMusicButtonEnabled) stopMusic();
},
);
const toggleMusicListener = (event: CustomEventInit) => {
try {
console.log('toggleMusic-update', event.detail);
if (!currentSettings.value?.enableMusicButton) return;
if (musicPlaying.value) {
stopMusic();
} else {
playMusic();
}
} catch (error) {
console.error(error);
}
};
onMounted(() => {
window.addEventListener('toggleMusic', toggleMusicListener);
});
onUnmounted(() => {
window.removeEventListener('toggleMusic', toggleMusicListener);
});
</script>
8 changes: 4 additions & 4 deletions src/defaults/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,22 +165,22 @@ export const settingsDefinitions: SettingsItems = {
shortcutMediaWindow: {
depends: 'enableKeyboardShortcuts',
group: 'advanced',
type: 'key',
type: 'shortcut',
},
shortcutMediaPrevious: {
depends: 'enableKeyboardShortcuts',
group: 'advanced',
type: 'key',
type: 'shortcut',
},
shortcutMediaNext: {
depends: 'enableKeyboardShortcuts',
group: 'advanced',
type: 'key',
type: 'shortcut',
},
shortcutMusic: {
depends: 'enableKeyboardShortcuts',
group: 'advanced',
type: 'key',
type: 'shortcut',
},
maxRes: {
depends: 'enableMediaDisplayButton',
Expand Down
22 changes: 22 additions & 0 deletions src/helpers/keyboardShortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,32 @@ import { SettingsValues } from 'src/types/settings';
const { registerShortcut, unregisterShortcut } = electronApi;

const shortcutCallbacks: Partial<Record<keyof SettingsValues, () => void>> = {
shortcutMediaNext: () => {
console.log('shortcutMediaNext');
},
shortcutMediaPrevious: () => {
console.log('shortcutMediaPrevious');
},
shortcutMediaWindow: () => {
const currentState = useCurrentStateStore();
const { mediaPlayer } = storeToRefs(currentState);
showMediaWindow(!mediaPlayer.value.windowVisible);
},
shortcutMusic: () => {
window.dispatchEvent(new CustomEvent('toggleMusic'));
},
};

const getCurrentShortcuts = () => {
const currentState = useCurrentStateStore();
const { currentSettings } = storeToRefs(currentState);
const shortcuts = [];
for (const shortcutName of Object.keys(shortcutCallbacks)) {
const shortcutVal =
currentSettings.value[shortcutName as keyof SettingsValues];
if (shortcutVal) shortcuts.push(shortcutVal);
}
return shortcuts;
};

const registerCustomShortcut = (
Expand Down Expand Up @@ -58,6 +79,7 @@ const unregisterAllCustomShortcuts = () => {
};

export {
getCurrentShortcuts,
registerAllCustomShortcuts,
registerCustomShortcut,
unregisterAllCustomShortcuts,
Expand Down
5 changes: 4 additions & 1 deletion src/i18n/en-US/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,5 +175,8 @@
"you-can-also-use-the-button-below-to-browse-for-files": "You can also browse for files manually if you prefer.",
"browse": "Browse",
"include-audio-description": "Include videos with audio descriptions",
"filter": "Filter"
"filter": "Filter",
"clear": "Clear",
"no-key-combination": "Enter a key combination now using your keyboard.",
"enter-key-combination": "Choose a shortcut"
}
5 changes: 4 additions & 1 deletion src/i18n/fr-CA/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -358,5 +358,8 @@
"applicationConfiguration": "Configuration de l'application",
"you-can-also-use-the-button-below-to-browse-for-files": "Vous pouvez également parcourir et sélectionner des fichiers manuellement, si vous le désirez.",
"include-audio-description": "Inclure les vidéos avec des descriptions audio",
"filter": "Filtre"
"filter": "Filtre",
"clear": "Supprimer",
"no-key-combination": "Entrez une combinaison de touches à l'aide de votre clavier maintenant.",
"enter-key-combination": "Choisir un raccourci"
}
32 changes: 7 additions & 25 deletions src/layouts/MainLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,10 @@ import ObsStatus from 'src/components/media/ObsStatus.vue';
// import ScenePicker from 'src/components/media/ScenePicker.vue';
import SongPicker from 'src/components/media/SongPicker.vue';
import SubtitlesButton from 'src/components/media/SubtitlesButton.vue';
import { cleanAdditionalMediaFolder, cleanLocalStorage } from 'src/helpers/cleanup';
import {
cleanAdditionalMediaFolder,
cleanLocalStorage,
} from 'src/helpers/cleanup';
import { getLookupPeriod } from 'src/helpers/date';
import { electronApi } from 'src/helpers/electron-api';
import {
Expand All @@ -379,9 +382,7 @@ import {
} from 'src/helpers/jw-media';
import {
registerAllCustomShortcuts,
registerCustomShortcut,
unregisterAllCustomShortcuts,
unregisterShortcut,
} from 'src/helpers/keyboardShortcuts';
import { createTemporaryNotification } from 'src/helpers/notifications';
import { useAppSettingsStore } from 'src/stores/app-settings';
Expand All @@ -392,7 +393,6 @@ import {
JwVideoCategory,
MediaItemsMediatorItem,
} from 'src/types/publications';
import { SettingsValues } from 'src/types/settings';
import { Ref, computed, onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
Expand Down Expand Up @@ -527,27 +527,9 @@ watch(
unregisterAllCustomShortcuts();
}
},
)
watch(
() => ({
shortcutMediaNext: currentSettings.value?.shortcutMediaNext,
shortcutMediaPrevious: currentSettings.value?.shortcutMediaPrevious,
shortcutMediaWindow: currentSettings.value?.shortcutMediaWindow,
shortcutMusic: currentSettings.value?.shortcutMusic,
}),
(newShortcuts, oldShortcuts) => {
Object.entries(oldShortcuts).forEach(([, oldShortcut]) => {
if (oldShortcut) unregisterShortcut(oldShortcut);
});
Object.entries(newShortcuts).forEach(
([shortcutName, newShortcut]) => {
if (newShortcut) registerCustomShortcut(shortcutName as keyof SettingsValues, newShortcut);
},
);
},
);
const dateOptions = (lookupDate: string) => {
const dateArray: Date[] = lookupPeriod.value.map((day) => day.date);
// @ts-expect-error "A spread argument must either have a tuple type or be passed to a rest parameter."
Expand Down Expand Up @@ -606,8 +588,8 @@ if (!migrations.value.includes('firstRun')) {
}
}
cleanLocalStorage()
cleanAdditionalMediaFolder()
cleanLocalStorage();
cleanAdditionalMediaFolder();
const getLocalFiles = async () => {
openFileDialog().then((result) => {
Expand Down
8 changes: 8 additions & 0 deletions src/pages/SettingsPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@
currentSettings[settingId as keyof SettingsItems] as string
"
/>
<ShortcutInput
:shortcutName="settingId as keyof SettingsItems"
v-else-if="item.type === 'shortcut'"
v-model="
currentSettings[settingId as keyof SettingsItems] as string
"
/>
<pre v-else>{{ item }}</pre>
</q-item-section>
</q-item>
Expand All @@ -159,6 +166,7 @@ import { storeToRefs } from 'pinia';
import DateInput from 'src/components/form-inputs/DateInput.vue';
import PathInput from 'src/components/form-inputs/PathInput.vue';
import SelectInput from 'src/components/form-inputs/SelectInput.vue';
import ShortcutInput from 'src/components/form-inputs/ShortcutInput.vue';
import SliderInput from 'src/components/form-inputs/SliderInput.vue';
import TextInput from 'src/components/form-inputs/TextInput.vue';
import TimeInput from 'src/components/form-inputs/TimeInput.vue';
Expand Down

0 comments on commit 2cc39c1

Please sign in to comment.