536 lines
16 KiB
JavaScript
536 lines
16 KiB
JavaScript
'use strict';
|
|
|
|
import Adw from 'gi://Adw';
|
|
import Gio from 'gi://Gio';
|
|
import Gtk from 'gi://Gtk';
|
|
|
|
import { ExtensionPreferences } from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
|
|
|
|
export default class QuotesPreferences extends ExtensionPreferences {
|
|
fillPreferencesWindow(window) {
|
|
const settings = this.getSettings();
|
|
|
|
// Behavior Settings Page
|
|
const behaviorPage = new Adw.PreferencesPage({
|
|
title: 'Behavior',
|
|
icon_name: 'preferences-system-symbolic',
|
|
});
|
|
window.add(behaviorPage);
|
|
|
|
const timingGroup = new Adw.PreferencesGroup({
|
|
title: 'Timing Settings',
|
|
description: 'Configure how quotes are rotated and synced',
|
|
});
|
|
behaviorPage.add(timingGroup);
|
|
|
|
// Rotation interval
|
|
const rotationRow = new Adw.SpinRow({
|
|
title: 'Rotation Interval',
|
|
subtitle: 'Time in seconds between quote changes',
|
|
adjustment: new Gtk.Adjustment({
|
|
lower: 10,
|
|
upper: 3600,
|
|
step_increment: 10,
|
|
page_increment: 60,
|
|
value: settings.get_int('rotation-interval'),
|
|
}),
|
|
});
|
|
timingGroup.add(rotationRow);
|
|
|
|
// Sync interval
|
|
const syncRow = new Adw.SpinRow({
|
|
title: 'Sync Interval',
|
|
subtitle: 'Time in seconds between API syncs',
|
|
adjustment: new Gtk.Adjustment({
|
|
lower: 300,
|
|
upper: 86400,
|
|
step_increment: 300,
|
|
page_increment: 3600,
|
|
value: settings.get_int('sync-interval'),
|
|
}),
|
|
});
|
|
timingGroup.add(syncRow);
|
|
|
|
// Show change indicator
|
|
const indicatorRow = new Adw.SwitchRow({
|
|
title: 'Show Change Indicator',
|
|
subtitle: 'Display countdown before quote changes',
|
|
active: settings.get_boolean('show-change-indicator'),
|
|
});
|
|
timingGroup.add(indicatorRow);
|
|
|
|
// Indicator duration
|
|
const indicatorDurationRow = new Adw.SpinRow({
|
|
title: 'Indicator Duration',
|
|
subtitle: 'Countdown duration in seconds',
|
|
adjustment: new Gtk.Adjustment({
|
|
lower: 1,
|
|
upper: 30,
|
|
step_increment: 1,
|
|
page_increment: 5,
|
|
value: settings.get_int('indicator-duration'),
|
|
}),
|
|
});
|
|
timingGroup.add(indicatorDurationRow);
|
|
|
|
// API Group
|
|
const apiGroup = new Adw.PreferencesGroup({
|
|
title: 'API Settings',
|
|
description: 'Configure external quote sources',
|
|
});
|
|
behaviorPage.add(apiGroup);
|
|
|
|
// API Providers
|
|
const apiProvidersRow = new Adw.ExpanderRow({
|
|
title: 'API Providers Configuration',
|
|
subtitle: 'Configure multiple quote providers',
|
|
});
|
|
|
|
const providersTextView = new Gtk.TextView({
|
|
buffer: new Gtk.TextBuffer(),
|
|
hexpand: true,
|
|
vexpand: true,
|
|
css_classes: ['card'],
|
|
monospace: true,
|
|
});
|
|
|
|
const scrolledProviders = new Gtk.ScrolledWindow({
|
|
child: providersTextView,
|
|
hexpand: true,
|
|
min_content_height: 200,
|
|
max_content_height: 400,
|
|
});
|
|
|
|
const providersContainer = new Gtk.Box({
|
|
orientation: Gtk.Orientation.VERTICAL,
|
|
spacing: 12,
|
|
margin_top: 12,
|
|
margin_bottom: 12,
|
|
margin_start: 12,
|
|
margin_end: 12,
|
|
});
|
|
|
|
const providersLabel = new Gtk.Label({
|
|
label: 'Provider Configuration (JSON):',
|
|
xalign: 0,
|
|
});
|
|
|
|
providersContainer.append(providersLabel);
|
|
providersContainer.append(scrolledProviders);
|
|
|
|
const providersPrefsRow = new Adw.PreferencesRow({
|
|
child: providersContainer,
|
|
});
|
|
|
|
apiProvidersRow.add_row(providersPrefsRow);
|
|
apiGroup.add(apiProvidersRow);
|
|
|
|
// Load current providers
|
|
const currentProviders = settings.get_string('api-providers');
|
|
try {
|
|
const formatted = JSON.stringify(JSON.parse(currentProviders), null, 2);
|
|
providersTextView.buffer.text = formatted;
|
|
} catch (e) {
|
|
providersTextView.buffer.text = currentProviders;
|
|
}
|
|
|
|
// Save on focus out
|
|
const focusController = new Gtk.EventControllerFocus();
|
|
focusController.connect('leave', () => {
|
|
const text = providersTextView.buffer.text;
|
|
try {
|
|
JSON.parse(text); // Validate JSON
|
|
settings.set_string('api-providers', text);
|
|
} catch (e) {
|
|
console.error('Invalid JSON in providers configuration');
|
|
}
|
|
});
|
|
providersTextView.add_controller(focusController);
|
|
|
|
// Appearance Settings Page
|
|
const appearancePage = new Adw.PreferencesPage({
|
|
title: 'Appearance',
|
|
icon_name: 'preferences-desktop-theme-symbolic',
|
|
});
|
|
window.add(appearancePage);
|
|
|
|
const displayGroup = new Adw.PreferencesGroup({
|
|
title: 'Display Settings',
|
|
description: 'Configure how quotes are displayed',
|
|
});
|
|
appearancePage.add(displayGroup);
|
|
|
|
// Font size
|
|
const fontSizeRow = new Adw.SpinRow({
|
|
title: 'Font Size',
|
|
subtitle: 'Size of quote text in pixels',
|
|
adjustment: new Gtk.Adjustment({
|
|
lower: 8,
|
|
upper: 32,
|
|
step_increment: 1,
|
|
page_increment: 2,
|
|
value: settings.get_int('font-size'),
|
|
}),
|
|
});
|
|
displayGroup.add(fontSizeRow);
|
|
|
|
// Text color
|
|
const colorRow = new Adw.EntryRow({
|
|
title: 'Text Color',
|
|
text: settings.get_string('text-color'),
|
|
});
|
|
displayGroup.add(colorRow);
|
|
|
|
// Maximum width
|
|
const widthRow = new Adw.SpinRow({
|
|
title: 'Maximum Width',
|
|
subtitle: 'Maximum characters before truncating',
|
|
adjustment: new Gtk.Adjustment({
|
|
lower: 20,
|
|
upper: 200,
|
|
step_increment: 5,
|
|
page_increment: 10,
|
|
value: settings.get_int('max-width'),
|
|
}),
|
|
});
|
|
displayGroup.add(widthRow);
|
|
|
|
// Storage Settings Page
|
|
const storagePage = new Adw.PreferencesPage({
|
|
title: 'Storage',
|
|
icon_name: 'folder-symbolic',
|
|
});
|
|
window.add(storagePage);
|
|
|
|
const storageGroup = new Adw.PreferencesGroup({
|
|
title: 'Storage Settings',
|
|
description: 'Configure data storage options',
|
|
});
|
|
storagePage.add(storageGroup);
|
|
|
|
// Quotes file
|
|
const quotesFileRow = new Adw.EntryRow({
|
|
title: 'Quotes File Name',
|
|
text: settings.get_string('quotes-file'),
|
|
});
|
|
storageGroup.add(quotesFileRow);
|
|
|
|
// Quotes Settings Page
|
|
const quotesPage = new Adw.PreferencesPage({
|
|
title: 'Quotes',
|
|
icon_name: 'text-editor-symbolic',
|
|
});
|
|
window.add(quotesPage);
|
|
|
|
const quotesSourceGroup = new Adw.PreferencesGroup({
|
|
title: 'Quote Priority',
|
|
description: 'Custom quotes and synced quotes are both saved to the JSON file',
|
|
});
|
|
quotesPage.add(quotesSourceGroup);
|
|
|
|
// Use custom quotes switch
|
|
const useCustomQuotesRow = new Adw.SwitchRow({
|
|
title: 'Prioritize Custom Quotes',
|
|
active: settings.get_boolean('use-custom-quotes'),
|
|
});
|
|
quotesSourceGroup.add(useCustomQuotesRow);
|
|
|
|
const customQuotesGroup = new Adw.PreferencesGroup({
|
|
title: 'Custom Quotes',
|
|
description: 'Manage your custom quotes with an easy-to-use interface.',
|
|
});
|
|
quotesPage.add(customQuotesGroup);
|
|
|
|
// Parse existing custom quotes
|
|
let customQuotesArray = [];
|
|
try {
|
|
customQuotesArray = JSON.parse(settings.get_string('custom-quotes'));
|
|
} catch (e) {
|
|
customQuotesArray = [];
|
|
}
|
|
|
|
// Create list box for quotes
|
|
const quotesListBox = new Gtk.ListBox({
|
|
selection_mode: Gtk.SelectionMode.SINGLE,
|
|
hexpand: true,
|
|
margin_top: 12,
|
|
margin_bottom: 12,
|
|
margin_start: 12,
|
|
margin_end: 12,
|
|
css_classes: ['boxed-list'],
|
|
});
|
|
|
|
// Function to create quote row
|
|
const createQuoteRow = (quote, index) => {
|
|
const row = new Gtk.ListBoxRow();
|
|
const box = new Gtk.Box({
|
|
orientation: Gtk.Orientation.VERTICAL,
|
|
spacing: 6,
|
|
margin_top: 12,
|
|
margin_bottom: 12,
|
|
margin_start: 12,
|
|
margin_end: 12,
|
|
});
|
|
|
|
const quoteLabel = new Gtk.Label({
|
|
label: `"${quote.text}"`,
|
|
wrap: true,
|
|
xalign: 0,
|
|
css_classes: ['title-4'],
|
|
});
|
|
|
|
const authorLabel = new Gtk.Label({
|
|
label: `— ${quote.author}`,
|
|
xalign: 0,
|
|
css_classes: ['dim-label'],
|
|
});
|
|
|
|
box.append(quoteLabel);
|
|
box.append(authorLabel);
|
|
row.set_child(box);
|
|
row._quoteIndex = index;
|
|
return row;
|
|
};
|
|
|
|
// Populate list with existing quotes
|
|
const refreshQuotesList = () => {
|
|
const child = quotesListBox.get_first_child();
|
|
while (child) {
|
|
const next = child.get_next_sibling();
|
|
quotesListBox.remove(child);
|
|
child = next;
|
|
}
|
|
|
|
customQuotesArray.forEach((quote, index) => {
|
|
quotesListBox.append(createQuoteRow(quote, index));
|
|
});
|
|
};
|
|
|
|
refreshQuotesList();
|
|
|
|
// Buttons container
|
|
const buttonsBox = new Gtk.Box({
|
|
orientation: Gtk.Orientation.HORIZONTAL,
|
|
spacing: 12,
|
|
margin_top: 12,
|
|
halign: Gtk.Align.END,
|
|
});
|
|
|
|
// Add button
|
|
const addButton = new Gtk.Button({
|
|
label: 'Add Quote',
|
|
css_classes: ['suggested-action'],
|
|
});
|
|
|
|
// Edit button
|
|
const editButton = new Gtk.Button({
|
|
label: 'Edit',
|
|
sensitive: false,
|
|
});
|
|
|
|
// Remove button
|
|
const removeButton = new Gtk.Button({
|
|
label: 'Remove',
|
|
css_classes: ['destructive-action'],
|
|
sensitive: false,
|
|
});
|
|
|
|
buttonsBox.append(addButton);
|
|
buttonsBox.append(editButton);
|
|
buttonsBox.append(removeButton);
|
|
|
|
// Main container
|
|
const quotesContainer = new Gtk.Box({
|
|
orientation: Gtk.Orientation.VERTICAL,
|
|
spacing: 12,
|
|
});
|
|
|
|
const scrolledWindow = new Gtk.ScrolledWindow({
|
|
child: quotesListBox,
|
|
hexpand: true,
|
|
vexpand: true,
|
|
min_content_height: 200,
|
|
max_content_height: 300,
|
|
});
|
|
|
|
quotesContainer.append(scrolledWindow);
|
|
quotesContainer.append(buttonsBox);
|
|
|
|
const quotesExpander = new Adw.ExpanderRow({
|
|
title: 'Custom Quotes Manager',
|
|
});
|
|
|
|
quotesExpander.add_row(new Adw.PreferencesRow({
|
|
child: quotesContainer,
|
|
}));
|
|
|
|
customQuotesGroup.add(quotesExpander);
|
|
|
|
// Handle list selection
|
|
quotesListBox.connect('row-selected', (listbox, row) => {
|
|
const hasSelection = row !== null;
|
|
editButton.sensitive = hasSelection;
|
|
removeButton.sensitive = hasSelection;
|
|
});
|
|
|
|
// Quote dialog function
|
|
const showQuoteDialog = (quote = null, index = -1) => {
|
|
const dialog = new Gtk.Dialog({
|
|
title: quote ? 'Edit Quote' : 'Add Quote',
|
|
modal: true,
|
|
transient_for: window,
|
|
});
|
|
|
|
dialog.add_button('Cancel', Gtk.ResponseType.CANCEL);
|
|
dialog.add_button(quote ? 'Save' : 'Add', Gtk.ResponseType.OK);
|
|
|
|
const contentArea = dialog.get_content_area();
|
|
contentArea.set_spacing(12);
|
|
contentArea.set_margin_top(12);
|
|
contentArea.set_margin_bottom(12);
|
|
contentArea.set_margin_start(12);
|
|
contentArea.set_margin_end(12);
|
|
|
|
const textEntry = new Gtk.Entry({
|
|
placeholder_text: 'Enter quote text...',
|
|
text: quote ? quote.text : '',
|
|
hexpand: true,
|
|
});
|
|
|
|
const authorEntry = new Gtk.Entry({
|
|
placeholder_text: 'Enter author name...',
|
|
text: quote ? quote.author : '',
|
|
hexpand: true,
|
|
});
|
|
|
|
const textLabel = new Gtk.Label({
|
|
label: 'Quote Text:',
|
|
xalign: 0,
|
|
});
|
|
|
|
const authorLabel = new Gtk.Label({
|
|
label: 'Author:',
|
|
xalign: 0,
|
|
});
|
|
|
|
contentArea.append(textLabel);
|
|
contentArea.append(textEntry);
|
|
contentArea.append(authorLabel);
|
|
contentArea.append(authorEntry);
|
|
|
|
dialog.connect('response', (dialog, response) => {
|
|
if (response === Gtk.ResponseType.OK) {
|
|
const text = textEntry.get_text().trim();
|
|
const author = authorEntry.get_text().trim();
|
|
|
|
if (text && author) {
|
|
const newQuote = { text, author };
|
|
|
|
if (index >= 0) {
|
|
customQuotesArray[index] = newQuote;
|
|
} else {
|
|
customQuotesArray.push(newQuote);
|
|
}
|
|
|
|
settings.set_string('custom-quotes', JSON.stringify(customQuotesArray));
|
|
refreshQuotesList();
|
|
}
|
|
}
|
|
dialog.destroy();
|
|
});
|
|
|
|
dialog.present();
|
|
};
|
|
|
|
// Button handlers
|
|
addButton.connect('clicked', () => {
|
|
showQuoteDialog();
|
|
});
|
|
|
|
editButton.connect('clicked', () => {
|
|
const selectedRow = quotesListBox.get_selected_row();
|
|
if (selectedRow) {
|
|
const index = selectedRow._quoteIndex;
|
|
showQuoteDialog(customQuotesArray[index], index);
|
|
}
|
|
});
|
|
|
|
removeButton.connect('clicked', () => {
|
|
const selectedRow = quotesListBox.get_selected_row();
|
|
if (selectedRow) {
|
|
const index = selectedRow._quoteIndex;
|
|
customQuotesArray.splice(index, 1);
|
|
settings.set_string('custom-quotes', JSON.stringify(customQuotesArray));
|
|
refreshQuotesList();
|
|
editButton.sensitive = false;
|
|
removeButton.sensitive = false;
|
|
}
|
|
});
|
|
|
|
// Bind settings
|
|
settings.bind(
|
|
'rotation-interval',
|
|
rotationRow,
|
|
'value',
|
|
Gio.SettingsBindFlags.DEFAULT
|
|
);
|
|
|
|
settings.bind(
|
|
'sync-interval',
|
|
syncRow,
|
|
'value',
|
|
Gio.SettingsBindFlags.DEFAULT
|
|
);
|
|
|
|
|
|
settings.bind(
|
|
'font-size',
|
|
fontSizeRow,
|
|
'value',
|
|
Gio.SettingsBindFlags.DEFAULT
|
|
);
|
|
|
|
settings.bind(
|
|
'text-color',
|
|
colorRow,
|
|
'text',
|
|
Gio.SettingsBindFlags.DEFAULT
|
|
);
|
|
|
|
settings.bind(
|
|
'max-width',
|
|
widthRow,
|
|
'value',
|
|
Gio.SettingsBindFlags.DEFAULT
|
|
);
|
|
|
|
settings.bind(
|
|
'quotes-file',
|
|
quotesFileRow,
|
|
'text',
|
|
Gio.SettingsBindFlags.DEFAULT
|
|
);
|
|
|
|
settings.bind(
|
|
'use-custom-quotes',
|
|
useCustomQuotesRow,
|
|
'active',
|
|
Gio.SettingsBindFlags.DEFAULT
|
|
);
|
|
|
|
settings.bind(
|
|
'show-change-indicator',
|
|
indicatorRow,
|
|
'active',
|
|
Gio.SettingsBindFlags.DEFAULT
|
|
);
|
|
|
|
settings.bind(
|
|
'indicator-duration',
|
|
indicatorDurationRow,
|
|
'value',
|
|
Gio.SettingsBindFlags.DEFAULT
|
|
);
|
|
|
|
}
|
|
} |