Skip to content

Commit 62047c3

Browse files
committed
Rewrite the editor page for the wallpaper upload
1 parent 8429cd4 commit 62047c3

4 files changed

Lines changed: 166 additions & 66 deletions

File tree

src/components/LocalWallpaperBrowser.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Gdk from 'gi://Gdk?version=4.0';
99
import {favoritesService} from '../services/favorites-service.js';
1010
import {thumbnailService} from '../services/thumbnail-service.js';
1111
import {createWallpaperCard} from './WallpaperCard.js';
12+
import {uploadWallpaper} from '../utils/wallpaper-utils.js';
1213

1314
export const LocalWallpaperBrowser = GObject.registerClass(
1415
{
@@ -153,6 +154,14 @@ export const LocalWallpaperBrowser = GObject.registerClass(
153154
openFolderButton.connect('clicked', () => this._openFolder());
154155
toolbarBox.append(openFolderButton);
155156

157+
// Upload/Select wallpaper button
158+
const uploadButton = new Gtk.Button({
159+
icon_name: 'upload-symbolic',
160+
tooltip_text: 'Upload wallpaper',
161+
});
162+
uploadButton.connect('clicked', () => this._selectWallpaper());
163+
toolbarBox.append(uploadButton);
164+
156165
return toolbarBox;
157166
}
158167

@@ -270,5 +279,18 @@ export const LocalWallpaperBrowser = GObject.registerClass(
270279
}
271280
);
272281
}
282+
283+
_selectWallpaper() {
284+
uploadWallpaper(
285+
this.get_root(),
286+
(destPath) => {
287+
// Refresh the browser to show the new wallpaper
288+
this._loadWallpapersAsync();
289+
290+
// Emit signal with the new path in ~/Wallpapers
291+
this.emit('wallpaper-selected', destPath);
292+
}
293+
);
294+
}
273295
}
274296
);

src/components/PaletteGenerator.js

Lines changed: 73 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import GdkPixbuf from 'gi://GdkPixbuf';
88

99
import {extractColorsFromWallpaperIM} from '../utils/imagemagick-color-extraction.js';
1010
import {adjustColor} from '../utils/color-utils.js';
11+
import {uploadWallpaper} from '../utils/wallpaper-utils.js';
12+
import {copyFile} from '../utils/file-utils.js';
1113
import {ColorSwatchGrid} from './palette/color-swatch-grid.js';
1214
import {ColorPickerDialog} from './palette/color-picker-dialog.js';
1315
import {WallpaperBrowser} from './WallpaperBrowser.js';
@@ -253,8 +255,14 @@ export const PaletteGenerator = GObject.registerClass(
253255
hexpand: true,
254256
});
255257

258+
// Empty state - shown when no wallpaper is loaded
259+
this._emptyStateBox = this._createEmptyState();
260+
viewBox.append(this._emptyStateBox);
261+
256262
// Wallpaper selection row - wrapped in PreferencesGroup for full width
257-
const wallpaperGroup = new Adw.PreferencesGroup();
263+
this._wallpaperGroup = new Adw.PreferencesGroup({
264+
visible: false, // Hidden initially until wallpaper is loaded
265+
});
258266

259267
const wallpaperRow = new Adw.ActionRow({
260268
title: 'Wallpaper',
@@ -267,14 +275,6 @@ export const PaletteGenerator = GObject.registerClass(
267275
valign: Gtk.Align.CENTER,
268276
});
269277

270-
const selectButton = new Gtk.Button({
271-
icon_name: 'document-open-symbolic',
272-
valign: Gtk.Align.CENTER,
273-
tooltip_text: 'Select wallpaper',
274-
});
275-
selectButton.connect('clicked', () => this._selectWallpaper());
276-
buttonBox.append(selectButton);
277-
278278
// ImageMagick extract button
279279
const imExtractButtonBox = new Gtk.Box({
280280
orientation: Gtk.Orientation.HORIZONTAL,
@@ -295,7 +295,6 @@ export const PaletteGenerator = GObject.registerClass(
295295
this._imExtractButton = new Gtk.Button({
296296
child: imExtractButtonBox,
297297
css_classes: ['suggested-action'],
298-
visible: false,
299298
tooltip_text: 'Extract colors from wallpaper',
300299
});
301300
this._imExtractButton.connect('clicked', () => {
@@ -326,7 +325,6 @@ export const PaletteGenerator = GObject.registerClass(
326325
child: editButtonBox,
327326
tooltip_text:
328327
'Edit wallpaper (apply filters before extraction)',
329-
visible: false,
330328
});
331329
this._editWallpaperBtn.connect('clicked', () =>
332330
this._openWallpaperEditor()
@@ -368,20 +366,20 @@ export const PaletteGenerator = GObject.registerClass(
368366

369367
wallpaperRow.add_suffix(buttonBox);
370368

371-
this._setupDropTarget(wallpaperRow);
372-
wallpaperGroup.add(wallpaperRow);
373-
viewBox.append(wallpaperGroup);
369+
this._wallpaperGroup.add(wallpaperRow);
374370

375-
// Wallpaper preview
371+
// Wallpaper preview (hidden with wallpaperGroup)
376372
this._wallpaperPreview = new Gtk.Picture({
377373
height_request: 200,
378374
can_shrink: true,
379375
content_fit: Gtk.ContentFit.CONTAIN,
380376
css_classes: ['card'],
381377
hexpand: true,
382-
visible: false,
378+
visible: false, // Hidden by default, shown when wallpaper loads
383379
});
384-
viewBox.append(this._wallpaperPreview);
380+
this._wallpaperGroup.add(this._wallpaperPreview);
381+
382+
viewBox.append(this._wallpaperGroup);
385383

386384
// Color swatch grid section
387385
const colorsHeaderBox = new Gtk.Box({
@@ -402,7 +400,6 @@ export const PaletteGenerator = GObject.registerClass(
402400
this._pickFromWallpaperBtn = new Gtk.Button({
403401
icon_name: 'color-select-symbolic',
404402
tooltip_text: 'Pick colors from wallpaper',
405-
visible: false,
406403
});
407404
this._pickFromWallpaperBtn.connect('clicked', () => {
408405
this._openWallpaperColorPicker();
@@ -470,56 +467,69 @@ export const PaletteGenerator = GObject.registerClass(
470467
this.emit('palette-generated', defaultColors);
471468
}
472469

473-
_setupDropTarget(widget) {
474-
const dropTarget = Gtk.DropTarget.new(
475-
Gio.File.$gtype,
476-
Gdk.DragAction.COPY
477-
);
470+
_createEmptyState() {
471+
const emptyBox = new Gtk.Box({
472+
orientation: Gtk.Orientation.VERTICAL,
473+
valign: Gtk.Align.CENTER,
474+
halign: Gtk.Align.CENTER,
475+
spacing: 24,
476+
margin_top: 48,
477+
margin_bottom: 48,
478+
hexpand: true,
479+
vexpand: true,
480+
});
478481

479-
dropTarget.connect('drop', (target, value) => {
480-
if (value instanceof Gio.File) {
481-
this.loadWallpaperWithoutExtraction(value.get_path());
482-
return true;
483-
}
484-
return false;
482+
const icon = new Gtk.Image({
483+
icon_name: 'folder-pictures-symbolic',
484+
pixel_size: 72,
485+
css_classes: ['dim-label'],
485486
});
486487

487-
widget.add_controller(dropTarget);
488-
}
488+
const titleLabel = new Gtk.Label({
489+
label: 'No Wallpaper Selected',
490+
css_classes: ['title-2'],
491+
});
492+
493+
const subtitleLabel = new Gtk.Label({
494+
label: 'Choose a wallpaper to begin creating your theme',
495+
css_classes: ['dim-label'],
496+
});
489497

490-
_selectWallpaper() {
491-
const dialog = new Gtk.FileDialog({title: 'Select Wallpaper'});
498+
const uploadButton = new Gtk.Button({
499+
label: 'Select Wallpaper',
500+
css_classes: ['pill', 'suggested-action'],
501+
halign: Gtk.Align.CENTER,
502+
});
503+
uploadButton.connect('clicked', () => {
504+
this._uploadWallpaper();
505+
});
492506

493-
const filter = new Gtk.FileFilter();
494-
filter.add_mime_type('image/png');
495-
filter.add_mime_type('image/jpeg');
496-
filter.add_mime_type('image/webp');
497-
filter.set_name('Images');
507+
emptyBox.append(icon);
508+
emptyBox.append(titleLabel);
509+
emptyBox.append(subtitleLabel);
510+
emptyBox.append(uploadButton);
498511

499-
const filterList = Gio.ListStore.new(Gtk.FileFilter.$gtype);
500-
filterList.append(filter);
501-
dialog.set_filters(filterList);
512+
return emptyBox;
513+
}
502514

503-
dialog.open(this.get_root(), null, (source, result) => {
504-
try {
505-
const file = dialog.open_finish(result);
506-
if (file) {
507-
this.loadWallpaperWithoutExtraction(file.get_path());
508-
}
509-
} catch (e) {
510-
if (
511-
!e.matches(Gtk.DialogError, Gtk.DialogError.DISMISSED)
512-
) {
513-
console.error('Error selecting file:', e.message);
514-
}
515+
_uploadWallpaper() {
516+
uploadWallpaper(
517+
this.get_root(),
518+
(destPath) => {
519+
// Load the wallpaper from ~/Wallpapers
520+
this.loadWallpaperWithoutExtraction(destPath);
515521
}
516-
});
522+
);
517523
}
518524

519525
loadWallpaper(path) {
520526
// Load wallpaper without extraction - user must click extract button
521527
this._currentWallpaper = path;
522528

529+
// Hide empty state, show wallpaper controls
530+
this._emptyStateBox.set_visible(false);
531+
this._wallpaperGroup.set_visible(true);
532+
523533
// Force complete reload by using texture instead of file
524534
// This ensures GTK doesn't cache the old image
525535
try {
@@ -534,10 +544,6 @@ export const PaletteGenerator = GObject.registerClass(
534544
this._wallpaperPreview.set_file(file);
535545
this._wallpaperPreview.set_visible(true);
536546
}
537-
538-
this._imExtractButton.set_visible(true);
539-
this._pickFromWallpaperBtn.set_visible(true);
540-
this._editWallpaperBtn.set_visible(true);
541547
}
542548

543549
_onWallpaperBrowserSelected(path) {
@@ -552,6 +558,10 @@ export const PaletteGenerator = GObject.registerClass(
552558
// For manual selection - just show wallpaper and extract button, don't auto-extract
553559
this._currentWallpaper = path;
554560

561+
// Hide empty state, show wallpaper controls
562+
this._emptyStateBox.set_visible(false);
563+
this._wallpaperGroup.set_visible(true);
564+
555565
// Force complete reload by using texture instead of file
556566
try {
557567
const pixbuf = GdkPixbuf.Pixbuf.new_from_file(path);
@@ -565,10 +575,6 @@ export const PaletteGenerator = GObject.registerClass(
565575
this._wallpaperPreview.set_file(file);
566576
this._wallpaperPreview.set_visible(true);
567577
}
568-
569-
this._imExtractButton.set_visible(true);
570-
this._pickFromWallpaperBtn.set_visible(true);
571-
this._editWallpaperBtn.set_visible(true);
572578
}
573579

574580
_extractColorsIM(imagePath) {
@@ -776,11 +782,12 @@ export const PaletteGenerator = GObject.registerClass(
776782
this._wallpaperPreview.set_visible(false);
777783
if (this._customWallpaperPreview) {
778784
this._customWallpaperPreview.set_file(null);
779-
this._customWallpaperPreview.set_visible(false);
780785
}
781-
this._imExtractButton.set_visible(false);
782-
this._pickFromWallpaperBtn.set_visible(false);
783-
this._editWallpaperBtn.set_visible(false);
786+
787+
// Show empty state, hide wallpaper controls
788+
this._emptyStateBox.set_visible(true);
789+
this._wallpaperGroup.set_visible(false);
790+
784791
this._swatchGrid.setLockedColors(new Array(16).fill(false)); // Reset all locks
785792
this._showLoading(false);
786793

src/icons/upload-symbolic.svg

Lines changed: 7 additions & 0 deletions
Loading

src/utils/wallpaper-utils.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import GLib from 'gi://GLib';
2+
import Gio from 'gi://Gio';
3+
import Gtk from 'gi://Gtk?version=4.0';
4+
5+
import {copyFile} from './file-utils.js';
6+
7+
/**
8+
* Opens a file picker dialog to select and upload a wallpaper to ~/Wallpapers
9+
* @param {Gtk.Widget} parent - Parent widget for the dialog
10+
* @param {Function} onSuccess - Callback function called with the wallpaper path on success
11+
* @param {Function} onError - Optional callback function called on error
12+
*/
13+
export function uploadWallpaper(parent, onSuccess, onError = null) {
14+
const dialog = new Gtk.FileDialog({title: 'Upload Wallpaper'});
15+
16+
const filter = new Gtk.FileFilter();
17+
filter.add_mime_type('image/png');
18+
filter.add_mime_type('image/jpeg');
19+
filter.add_mime_type('image/webp');
20+
filter.set_name('Images');
21+
22+
const filterList = Gio.ListStore.new(Gtk.FileFilter.$gtype);
23+
filterList.append(filter);
24+
dialog.set_filters(filterList);
25+
26+
dialog.open(parent, null, (source, result) => {
27+
try {
28+
const file = dialog.open_finish(result);
29+
if (file) {
30+
const sourcePath = file.get_path();
31+
const fileName = GLib.path_get_basename(sourcePath);
32+
const wallpapersPath = GLib.build_filenamev([
33+
GLib.get_home_dir(),
34+
'Wallpapers',
35+
]);
36+
const destPath = GLib.build_filenamev([
37+
wallpapersPath,
38+
fileName,
39+
]);
40+
41+
// Copy file to ~/Wallpapers
42+
const success = copyFile(sourcePath, destPath);
43+
if (success) {
44+
console.log(`Wallpaper uploaded to: ${destPath}`);
45+
if (onSuccess) {
46+
onSuccess(destPath);
47+
}
48+
} else {
49+
console.error('Failed to copy wallpaper to ~/Wallpapers');
50+
if (onError) {
51+
onError(new Error('Failed to copy wallpaper'));
52+
}
53+
}
54+
}
55+
} catch (e) {
56+
if (!e.matches(Gtk.DialogError, Gtk.DialogError.DISMISSED)) {
57+
console.error('Error selecting file:', e.message);
58+
if (onError) {
59+
onError(e);
60+
}
61+
}
62+
}
63+
});
64+
}

0 commit comments

Comments
 (0)