window size saved to config, smaller font in track-list
This commit is contained in:
@@ -107,6 +107,11 @@ func NewYellowJacketApp(
|
|||||||
return yjApp, nil
|
return yjApp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WindowConfig returns the window configuration for use by the host.
|
||||||
|
func (yj *YellowJacketApp) WindowConfig() *config.WindowConfig {
|
||||||
|
return yj.appConfig.Window
|
||||||
|
}
|
||||||
|
|
||||||
var startupErr error
|
var startupErr error
|
||||||
|
|
||||||
// OnStartup initializes components that require the Wails runtime context.
|
// OnStartup initializes components that require the Wails runtime context.
|
||||||
@@ -143,6 +148,23 @@ func (yj *YellowJacketApp) OnStartup(ctx context.Context) {
|
|||||||
yj.FEBindings = append(yj.FEBindings, yj.player)
|
yj.FEBindings = append(yj.FEBindings, yj.player)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnBeforeClose captures window state while the window is still alive.
|
||||||
|
func (yj *YellowJacketApp) OnBeforeClose(ctx context.Context) bool {
|
||||||
|
w, h := wailsruntime.WindowGetSize(ctx)
|
||||||
|
|
||||||
|
yj.appConfig.Window.Width = w
|
||||||
|
yj.appConfig.Window.Height = h
|
||||||
|
|
||||||
|
if err := yj.appConfig.Save(); err != nil {
|
||||||
|
yj.logger.Error(
|
||||||
|
"Failed to save window state",
|
||||||
|
"err", err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// OnShutdown saves player state and cleans up resources before the application exits.
|
// OnShutdown saves player state and cleans up resources before the application exits.
|
||||||
func (yj *YellowJacketApp) OnShutdown(_ context.Context) {
|
func (yj *YellowJacketApp) OnShutdown(_ context.Context) {
|
||||||
if yj.player != nil {
|
if yj.player != nil {
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ type Config struct {
|
|||||||
serveMux *http.ServeMux
|
serveMux *http.ServeMux
|
||||||
filePath string // required
|
filePath string // required
|
||||||
Library *library.Config `form:"Library" schema:"library,required"`
|
Library *library.Config `form:"Library" schema:"library,required"`
|
||||||
|
|
||||||
|
Window *WindowConfig `toml:"Window"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig creates a new config by loading it from disk.
|
// NewConfig creates a new config by loading it from disk.
|
||||||
@@ -36,6 +38,7 @@ func NewConfig(logger *slog.Logger) (*Config, error) {
|
|||||||
filePath: path.Join(confDir, "config.toml"),
|
filePath: path.Join(confDir, "config.toml"),
|
||||||
serveMux: http.NewServeMux(),
|
serveMux: http.NewServeMux(),
|
||||||
}
|
}
|
||||||
|
conf.applyDefaults()
|
||||||
conf.logger = logger.WithGroup("config").With("config", conf)
|
conf.logger = logger.WithGroup("config").With("config", conf)
|
||||||
conf.serveMux.HandleFunc("/", conf.handle)
|
conf.serveMux.HandleFunc("/", conf.handle)
|
||||||
|
|
||||||
@@ -99,6 +102,8 @@ func (c *Config) Load() error {
|
|||||||
return fmt.Errorf("problem parsing config file %s: %w", c.filePath, err)
|
return fmt.Errorf("problem parsing config file %s: %w", c.filePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.applyDefaults()
|
||||||
|
|
||||||
// validate the config
|
// validate the config
|
||||||
if err = c.Validate(); err != nil {
|
if err = c.Validate(); err != nil {
|
||||||
return fmt.Errorf("invalid config file at %s: %w", c.filePath, err)
|
return fmt.Errorf("invalid config file at %s: %w", c.filePath, err)
|
||||||
@@ -130,6 +135,15 @@ func (c *Config) Save() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// applyDefaults ensures all config sections have valid defaults.
|
||||||
|
func (c *Config) applyDefaults() {
|
||||||
|
if c.Window == nil {
|
||||||
|
c.Window = NewDefaultWindowConfig()
|
||||||
|
} else {
|
||||||
|
c.Window.applyDefaults()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SetContext sets the Wails runtime context for event emission.
|
// SetContext sets the Wails runtime context for event emission.
|
||||||
func (c *Config) SetContext(ctx context.Context) {
|
func (c *Config) SetContext(ctx context.Context) {
|
||||||
c.ctx = ctx
|
c.ctx = ctx
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultWidth is the default window width in pixels.
|
||||||
|
DefaultWidth = 512
|
||||||
|
// DefaultHeight is the default window height in pixels.
|
||||||
|
DefaultHeight = 384
|
||||||
|
)
|
||||||
|
|
||||||
|
// WindowConfig holds window size preferences.
|
||||||
|
type WindowConfig struct {
|
||||||
|
Width int `toml:"Width"`
|
||||||
|
Height int `toml:"Height"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultWindowConfig returns a WindowConfig with sensible defaults.
|
||||||
|
func NewDefaultWindowConfig() *WindowConfig {
|
||||||
|
return &WindowConfig{
|
||||||
|
Width: DefaultWidth,
|
||||||
|
Height: DefaultHeight,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyDefaults fills in zero-value fields with defaults.
|
||||||
|
func (w *WindowConfig) applyDefaults() {
|
||||||
|
if w.Width <= 0 {
|
||||||
|
w.Width = DefaultWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.Height <= 0 {
|
||||||
|
w.Height = DefaultHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,30 +16,30 @@ import type { PlaylistPicker } from '@components/playlist-picker/playlist-picker
|
|||||||
|
|
||||||
@customElement('track-list')
|
@customElement('track-list')
|
||||||
export class TrackList extends LitElement {
|
export class TrackList extends LitElement {
|
||||||
private player = new PlayerController(this);
|
private player = new PlayerController(this);
|
||||||
private queue = new QueueController(this);
|
private queue = new QueueController(this);
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private tracks: library.Track[] = [];
|
private tracks: library.Track[] = [];
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private selectedTracks: Set<string> = new Set();
|
private selectedTracks: Set<string> = new Set();
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private contextMenuOpen = false;
|
private contextMenuOpen = false;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private playlistSubmenuOpen = false;
|
private playlistSubmenuOpen = false;
|
||||||
|
|
||||||
@query('#context-menu')
|
@query('#context-menu')
|
||||||
private contextMenuPopup!: HTMLElement;
|
private contextMenuPopup!: HTMLElement;
|
||||||
|
|
||||||
@query('#playlist-submenu')
|
@query('#playlist-submenu')
|
||||||
private playlistSubmenuPopup!: HTMLElement;
|
private playlistSubmenuPopup!: HTMLElement;
|
||||||
|
|
||||||
private closeHandler = () => this.closeContextMenu();
|
private closeHandler = () => this.closeContextMenu();
|
||||||
|
|
||||||
static override styles = css`
|
static override styles = css`
|
||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -64,6 +64,7 @@ export class TrackList extends LitElement {
|
|||||||
.track-row {
|
.track-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 80px;
|
grid-template-columns: 1fr 1fr 80px;
|
||||||
|
font-size: 12px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-bottom: 1px solid #333;
|
border-bottom: 1px solid #333;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -152,198 +153,198 @@ export class TrackList extends LitElement {
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
override connectedCallback() {
|
override connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.loadTracks();
|
this.loadTracks();
|
||||||
document.addEventListener('click', this.closeHandler);
|
document.addEventListener('click', this.closeHandler);
|
||||||
document.addEventListener('contextmenu', this.closeHandler);
|
document.addEventListener('contextmenu', this.closeHandler);
|
||||||
}
|
|
||||||
|
|
||||||
override disconnectedCallback() {
|
|
||||||
super.disconnectedCallback();
|
|
||||||
document.removeEventListener('click', this.closeHandler);
|
|
||||||
document.removeEventListener('contextmenu', this.closeHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
async loadTracks() {
|
|
||||||
try {
|
|
||||||
const tracks = await GetAllTracks();
|
|
||||||
this.tracks = tracks;
|
|
||||||
this.selectedTracks = new Set();
|
|
||||||
|
|
||||||
if (tracks[0]) {
|
|
||||||
LogPrint(tracks[0].TrackName);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error loading tracks:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getSelectedFilePaths(): string[] {
|
|
||||||
return this.tracks
|
|
||||||
.filter((t) => this.selectedTracks.has(t.FilePath))
|
|
||||||
.map((t) => t.FilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onTrackRowClick(e: MouseEvent, track: library.Track) {
|
|
||||||
const isCtrl = e.ctrlKey || e.metaKey;
|
|
||||||
|
|
||||||
if (isCtrl) {
|
|
||||||
const next = new Set(this.selectedTracks);
|
|
||||||
|
|
||||||
if (next.has(track.FilePath)) {
|
|
||||||
next.delete(track.FilePath);
|
|
||||||
} else {
|
|
||||||
next.add(track.FilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectedTracks = next;
|
|
||||||
} else {
|
|
||||||
this.selectedTracks = new Set([track.FilePath]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private onTrackRowDblClick(track: library.Track) {
|
|
||||||
this.selectedTracks = new Set();
|
|
||||||
this.queue.setQueue([track.FilePath], 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private onTrackContextMenu(e: MouseEvent, track: library.Track) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
if (!this.selectedTracks.has(track.FilePath)) {
|
|
||||||
this.selectedTracks = new Set([track.FilePath]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.contextMenuOpen = true;
|
override disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
// Position the popup at the mouse cursor using a virtual anchor.
|
document.removeEventListener('click', this.closeHandler);
|
||||||
this.updateComplete.then(() => {
|
document.removeEventListener('contextmenu', this.closeHandler);
|
||||||
const popup = this.contextMenuPopup;
|
|
||||||
|
|
||||||
if (popup) {
|
|
||||||
(popup as any).anchor = {
|
|
||||||
getBoundingClientRect() {
|
|
||||||
return {
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
x: e.clientX,
|
|
||||||
y: e.clientY,
|
|
||||||
top: e.clientY,
|
|
||||||
left: e.clientX,
|
|
||||||
right: e.clientX,
|
|
||||||
bottom: e.clientY,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
(popup as any).active = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onContextMenuAction(action: string) {
|
|
||||||
const filePaths = this.getSelectedFilePaths();
|
|
||||||
|
|
||||||
if (filePaths.length === 0) return;
|
|
||||||
|
|
||||||
switch (action) {
|
|
||||||
case 'play':
|
|
||||||
this.queue.setQueue(filePaths, 0);
|
|
||||||
break;
|
|
||||||
case 'add-to-queue':
|
|
||||||
this.queue.addTracksToQueue(filePaths);
|
|
||||||
break;
|
|
||||||
case 'play-next':
|
|
||||||
this.queue.playTracksNext(filePaths);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.closeContextMenu(true);
|
async loadTracks() {
|
||||||
}
|
try {
|
||||||
|
const tracks = await GetAllTracks();
|
||||||
|
this.tracks = tracks;
|
||||||
|
this.selectedTracks = new Set();
|
||||||
|
|
||||||
private closeContextMenu(clearSelection = false) {
|
if (tracks[0]) {
|
||||||
if (!this.contextMenuOpen) return;
|
LogPrint(tracks[0].TrackName);
|
||||||
|
}
|
||||||
this.closePlaylistSubmenu();
|
} catch (error) {
|
||||||
this.contextMenuOpen = false;
|
console.error('Error loading tracks:', error);
|
||||||
|
}
|
||||||
if (clearSelection) {
|
|
||||||
this.selectedTracks = new Set();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const popup = this.contextMenuPopup;
|
private getSelectedFilePaths(): string[] {
|
||||||
|
return this.tracks
|
||||||
if (popup) {
|
.filter((t) => this.selectedTracks.has(t.FilePath))
|
||||||
(popup as any).active = false;
|
.map((t) => t.FilePath);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async showPlaylistSubmenu() {
|
|
||||||
if (this.playlistSubmenuOpen) return;
|
|
||||||
|
|
||||||
this.playlistSubmenuOpen = true;
|
|
||||||
|
|
||||||
await this.updateComplete;
|
|
||||||
|
|
||||||
const submenu = this.playlistSubmenuPopup;
|
|
||||||
const trigger = this.shadowRoot?.querySelector('.submenu-item');
|
|
||||||
|
|
||||||
if (submenu && trigger) {
|
|
||||||
(submenu as any).anchor = trigger;
|
|
||||||
(submenu as any).active = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const picker = this.shadowRoot?.querySelector(
|
private onTrackRowClick(e: MouseEvent, track: library.Track) {
|
||||||
'playlist-picker',
|
const isCtrl = e.ctrlKey || e.metaKey;
|
||||||
) as PlaylistPicker | null;
|
|
||||||
|
|
||||||
picker?.reset();
|
if (isCtrl) {
|
||||||
}
|
const next = new Set(this.selectedTracks);
|
||||||
|
|
||||||
private closePlaylistSubmenu() {
|
if (next.has(track.FilePath)) {
|
||||||
if (!this.playlistSubmenuOpen) return;
|
next.delete(track.FilePath);
|
||||||
|
} else {
|
||||||
|
next.add(track.FilePath);
|
||||||
|
}
|
||||||
|
|
||||||
this.playlistSubmenuOpen = false;
|
this.selectedTracks = next;
|
||||||
|
} else {
|
||||||
const submenu = this.playlistSubmenuPopup;
|
this.selectedTracks = new Set([track.FilePath]);
|
||||||
|
}
|
||||||
if (submenu) {
|
|
||||||
(submenu as any).active = false;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private onPlaylistActionComplete = () => {
|
private onTrackRowDblClick(track: library.Track) {
|
||||||
this.closeContextMenu(true);
|
this.selectedTracks = new Set();
|
||||||
};
|
this.queue.setQueue([track.FilePath], 0);
|
||||||
|
}
|
||||||
|
|
||||||
private isActiveTrack(track: library.Track): boolean {
|
private onTrackContextMenu(e: MouseEvent, track: library.Track) {
|
||||||
const currentTrack = this.player.currentTrack;
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
if (!currentTrack) return false;
|
if (!this.selectedTracks.has(track.FilePath)) {
|
||||||
|
this.selectedTracks = new Set([track.FilePath]);
|
||||||
|
}
|
||||||
|
|
||||||
return currentTrack.filePath === track.FilePath;
|
this.contextMenuOpen = true;
|
||||||
}
|
|
||||||
|
|
||||||
private renderTrackRow = (track: library.Track): unknown => {
|
// Position the popup at the mouse cursor using a virtual anchor.
|
||||||
const active = this.isActiveTrack(track);
|
this.updateComplete.then(() => {
|
||||||
const selected = this.selectedTracks.has(track.FilePath);
|
const popup = this.contextMenuPopup;
|
||||||
|
|
||||||
const classes = [
|
if (popup) {
|
||||||
'track-row',
|
(popup as any).anchor = {
|
||||||
active ? 'active' : '',
|
getBoundingClientRect() {
|
||||||
selected ? 'selected' : '',
|
return {
|
||||||
]
|
width: 0,
|
||||||
.filter(Boolean)
|
height: 0,
|
||||||
.join(' ');
|
x: e.clientX,
|
||||||
|
y: e.clientY,
|
||||||
|
top: e.clientY,
|
||||||
|
left: e.clientX,
|
||||||
|
right: e.clientX,
|
||||||
|
bottom: e.clientY,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
(popup as any).active = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
private onContextMenuAction(action: string) {
|
||||||
|
const filePaths = this.getSelectedFilePaths();
|
||||||
|
|
||||||
|
if (filePaths.length === 0) return;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case 'play':
|
||||||
|
this.queue.setQueue(filePaths, 0);
|
||||||
|
break;
|
||||||
|
case 'add-to-queue':
|
||||||
|
this.queue.addTracksToQueue(filePaths);
|
||||||
|
break;
|
||||||
|
case 'play-next':
|
||||||
|
this.queue.playTracksNext(filePaths);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.closeContextMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private closeContextMenu(clearSelection = false) {
|
||||||
|
if (!this.contextMenuOpen) return;
|
||||||
|
|
||||||
|
this.closePlaylistSubmenu();
|
||||||
|
this.contextMenuOpen = false;
|
||||||
|
|
||||||
|
if (clearSelection) {
|
||||||
|
this.selectedTracks = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
const popup = this.contextMenuPopup;
|
||||||
|
|
||||||
|
if (popup) {
|
||||||
|
(popup as any).active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async showPlaylistSubmenu() {
|
||||||
|
if (this.playlistSubmenuOpen) return;
|
||||||
|
|
||||||
|
this.playlistSubmenuOpen = true;
|
||||||
|
|
||||||
|
await this.updateComplete;
|
||||||
|
|
||||||
|
const submenu = this.playlistSubmenuPopup;
|
||||||
|
const trigger = this.shadowRoot?.querySelector('.submenu-item');
|
||||||
|
|
||||||
|
if (submenu && trigger) {
|
||||||
|
(submenu as any).anchor = trigger;
|
||||||
|
(submenu as any).active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const picker = this.shadowRoot?.querySelector(
|
||||||
|
'playlist-picker',
|
||||||
|
) as PlaylistPicker | null;
|
||||||
|
|
||||||
|
picker?.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private closePlaylistSubmenu() {
|
||||||
|
if (!this.playlistSubmenuOpen) return;
|
||||||
|
|
||||||
|
this.playlistSubmenuOpen = false;
|
||||||
|
|
||||||
|
const submenu = this.playlistSubmenuPopup;
|
||||||
|
|
||||||
|
if (submenu) {
|
||||||
|
(submenu as any).active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onPlaylistActionComplete = () => {
|
||||||
|
this.closeContextMenu(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
private isActiveTrack(track: library.Track): boolean {
|
||||||
|
const currentTrack = this.player.currentTrack;
|
||||||
|
|
||||||
|
if (!currentTrack) return false;
|
||||||
|
|
||||||
|
return currentTrack.filePath === track.FilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderTrackRow = (track: library.Track): unknown => {
|
||||||
|
const active = this.isActiveTrack(track);
|
||||||
|
const selected = this.selectedTracks.has(track.FilePath);
|
||||||
|
|
||||||
|
const classes = [
|
||||||
|
'track-row',
|
||||||
|
active ? 'active' : '',
|
||||||
|
selected ? 'selected' : '',
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ');
|
||||||
|
|
||||||
|
return html`
|
||||||
<div
|
<div
|
||||||
class=${classes}
|
class=${classes}
|
||||||
@click=${(e: MouseEvent) => this.onTrackRowClick(e, track)}
|
@click=${(e: MouseEvent) => this.onTrackRowClick(e, track)}
|
||||||
@dblclick=${() => this.onTrackRowDblClick(track)}
|
@dblclick=${() => this.onTrackRowDblClick(track)}
|
||||||
@contextmenu=${(e: MouseEvent) =>
|
@contextmenu=${(e: MouseEvent) =>
|
||||||
this.onTrackContextMenu(e, track)}
|
this.onTrackContextMenu(e, track)}
|
||||||
>
|
>
|
||||||
<div class="track-name">${track.TrackName}</div>
|
<div class="track-name">${track.TrackName}</div>
|
||||||
<div class="artist-name">${track.ArtistName}</div>
|
<div class="artist-name">${track.ArtistName}</div>
|
||||||
@@ -352,13 +353,13 @@ export class TrackList extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
override render() {
|
override render() {
|
||||||
return html`
|
return html`
|
||||||
${this.tracks.length === 0
|
${this.tracks.length === 0
|
||||||
? html`<p>Loading tracks...</p>`
|
? html`<p>Loading tracks...</p>`
|
||||||
: html`
|
: html`
|
||||||
<div class="header-row">
|
<div class="header-row">
|
||||||
<span>Track Name</span>
|
<span>Track Name</span>
|
||||||
<span>Artist</span>
|
<span>Artist</span>
|
||||||
@@ -378,7 +379,7 @@ export class TrackList extends LitElement {
|
|||||||
.active=${this.contextMenuOpen}
|
.active=${this.contextMenuOpen}
|
||||||
>
|
>
|
||||||
${this.contextMenuOpen
|
${this.contextMenuOpen
|
||||||
? html`
|
? html`
|
||||||
<div class="context-menu-panel">
|
<div class="context-menu-panel">
|
||||||
<wa-dropdown-item
|
<wa-dropdown-item
|
||||||
@click=${() => this.onContextMenuAction('play')}
|
@click=${() => this.onContextMenuAction('play')}
|
||||||
@@ -402,9 +403,9 @@ export class TrackList extends LitElement {
|
|||||||
class="submenu-item"
|
class="submenu-item"
|
||||||
@mouseenter=${() => this.showPlaylistSubmenu()}
|
@mouseenter=${() => this.showPlaylistSubmenu()}
|
||||||
@click=${(e: Event) => {
|
@click=${(e: Event) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
void this.showPlaylistSubmenu();
|
void this.showPlaylistSubmenu();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<wa-icon slot="icon" name="plus"></wa-icon>
|
<wa-icon slot="icon" name="plus"></wa-icon>
|
||||||
Add to Playlist
|
Add to Playlist
|
||||||
@@ -412,7 +413,7 @@ export class TrackList extends LitElement {
|
|||||||
</wa-dropdown-item>
|
</wa-dropdown-item>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</wa-popup>
|
</wa-popup>
|
||||||
|
|
||||||
<wa-popup
|
<wa-popup
|
||||||
@@ -421,15 +422,15 @@ export class TrackList extends LitElement {
|
|||||||
.active=${this.playlistSubmenuOpen}
|
.active=${this.playlistSubmenuOpen}
|
||||||
>
|
>
|
||||||
${this.playlistSubmenuOpen && this.selectedTracks.size > 0
|
${this.playlistSubmenuOpen && this.selectedTracks.size > 0
|
||||||
? html`
|
? html`
|
||||||
<playlist-picker
|
<playlist-picker
|
||||||
.filePaths=${this.getSelectedFilePaths()}
|
.filePaths=${this.getSelectedFilePaths()}
|
||||||
@playlist-action-complete=${this.onPlaylistActionComplete}
|
@playlist-action-complete=${this.onPlaylistActionComplete}
|
||||||
@click=${(e: Event) => e.stopPropagation()}
|
@click=${(e: Event) => e.stopPropagation()}
|
||||||
></playlist-picker>
|
></playlist-picker>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</wa-popup>
|
</wa-popup>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,10 +57,12 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create application with options
|
// Create application with options
|
||||||
|
winCfg := yjApp.WindowConfig()
|
||||||
|
|
||||||
err = wails.Run(&options.App{
|
err = wails.Run(&options.App{
|
||||||
Title: "yellowjacket",
|
Title: "yellowjacket",
|
||||||
Width: 512,
|
Width: winCfg.Width,
|
||||||
Height: 384,
|
Height: winCfg.Height,
|
||||||
Logger: logging.NewLogger(
|
Logger: logging.NewLogger(
|
||||||
sLogger,
|
sLogger,
|
||||||
[]string{},
|
[]string{},
|
||||||
@@ -69,6 +71,7 @@ func main() {
|
|||||||
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
|
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
|
||||||
OnStartup: yjApp.OnStartup,
|
OnStartup: yjApp.OnStartup,
|
||||||
OnDomReady: yjApp.OnDomReady,
|
OnDomReady: yjApp.OnDomReady,
|
||||||
|
OnBeforeClose: yjApp.OnBeforeClose,
|
||||||
OnShutdown: yjApp.OnShutdown,
|
OnShutdown: yjApp.OnShutdown,
|
||||||
Bind: yjApp.FEBindings,
|
Bind: yjApp.FEBindings,
|
||||||
MinWidth: 512,
|
MinWidth: 512,
|
||||||
|
|||||||
Reference in New Issue
Block a user