mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
Wallpaper fav system
This commit is contained in:
@@ -1791,6 +1791,8 @@
|
||||
"apikey-placeholder": "Enter your Wallhaven API Key",
|
||||
"apply-all-monitors-description": "Apply the selected wallpaper to all monitors.",
|
||||
"apply-all-monitors-label": "Apply to all monitors",
|
||||
"color-extraction-disabled": "Enable wallpaper color extraction",
|
||||
"color-extraction-enabled": "Enable predetermined colors",
|
||||
"categories-anime": "Anime",
|
||||
"categories-label": "Categories",
|
||||
"categories-people": "People",
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
// Add default keybinds (1-6) to session menu power options if none are defined
|
||||
function migrate(adapter, logger, rawJson) {
|
||||
logger.i("Settings", "Migrating settings to v53");
|
||||
|
||||
const powerOptions = rawJson?.sessionMenu?.powerOptions;
|
||||
if (!powerOptions || !Array.isArray(powerOptions))
|
||||
return true;
|
||||
|
||||
// Check if any power option has a keybind defined
|
||||
const hasAnyKeybind = powerOptions.some(opt => opt.keybind && opt.keybind !== "");
|
||||
if (hasAnyKeybind)
|
||||
return true;
|
||||
|
||||
// No keybinds defined — apply defaults matching the action order
|
||||
const defaultKeybinds = {
|
||||
"lock": "1",
|
||||
"suspend": "2",
|
||||
"hibernate": "3",
|
||||
"reboot": "4",
|
||||
"logout": "5",
|
||||
"shutdown": "6"
|
||||
};
|
||||
|
||||
for (let i = 0; i < powerOptions.length; i++) {
|
||||
const action = powerOptions[i].action;
|
||||
if (defaultKeybinds[action]) {
|
||||
adapter.sessionMenu.powerOptions[i].keybind = defaultKeybinds[action];
|
||||
logger.i("Settings", "Set keybind '" + defaultKeybinds[action] + "' for session menu action: " + action);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,8 @@ QtObject {
|
||||
47: migration47Component,
|
||||
48: migration48Component,
|
||||
49: migration49Component,
|
||||
50: migration50Component
|
||||
50: migration50Component,
|
||||
53: migration53Component
|
||||
})
|
||||
|
||||
// Migration components
|
||||
@@ -46,4 +47,5 @@ QtObject {
|
||||
property Component migration48Component: Migration48 {}
|
||||
property Component migration49Component: Migration49 {}
|
||||
property Component migration50Component: Migration50 {}
|
||||
property Component migration53Component: Migration53 {}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ Singleton {
|
||||
- Default cache directory: ~/.cache/noctalia
|
||||
*/
|
||||
readonly property alias data: adapter // Used to access via Settings.data.xxx.yyy
|
||||
readonly property int settingsVersion: 52
|
||||
readonly property int settingsVersion: 53
|
||||
readonly property bool isDebug: Quickshell.env("NOCTALIA_DEBUG") === "1"
|
||||
readonly property string shellName: "noctalia"
|
||||
readonly property string configDir: Quickshell.env("NOCTALIA_CONFIG_DIR") || (Quickshell.env("XDG_CONFIG_HOME") || Quickshell.env("HOME") + "/.config") + "/" + shellName + "/"
|
||||
@@ -393,6 +393,8 @@ Singleton {
|
||||
|
||||
property string wallhavenResolutionHeight: ""
|
||||
property string sortOrder: "name" // "name", "name_desc", "date", "date_desc", "random"
|
||||
property list<var> favorites: []
|
||||
// Format: [{ "path": "/path/to/wallpaper.jpg", "colorScheme": "...", "darkMode": true, "useWallpaperColors": true, "generationMethod": "tonal-spot" }]
|
||||
}
|
||||
|
||||
// applauncher
|
||||
|
||||
@@ -114,8 +114,9 @@ SmartPanel {
|
||||
let view = contentItem.screenRepeater.itemAt(contentItem.currentScreenIndex);
|
||||
if (view?.gridView?.hasActiveFocus) {
|
||||
let gridView = view.gridView;
|
||||
if (gridView.currentIndex >= 0 && gridView.currentIndex < gridView.model.length) {
|
||||
view.selectItem(gridView.model[gridView.currentIndex]);
|
||||
if (gridView.currentIndex >= 0 && gridView.currentIndex < gridView.model.count) {
|
||||
var item = gridView.model.get(gridView.currentIndex);
|
||||
view.selectItem(item.path, item.isDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -437,17 +438,72 @@ SmartPanel {
|
||||
}
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: Settings.data.colorSchemes.darkMode ? "moon" : "sun"
|
||||
tooltipText: Settings.data.colorSchemes.darkMode ? I18n.tr("tooltips.switch-to-light-mode") : I18n.tr("tooltips.switch-to-dark-mode")
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: Settings.data.colorSchemes.darkMode = !Settings.data.colorSchemes.darkMode
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "color-swatch"
|
||||
tooltipText: Settings.data.colorSchemes.useWallpaperColors
|
||||
? I18n.tr("wallpaper.panel.color-extraction-enabled")
|
||||
: I18n.tr("wallpaper.panel.color-extraction-disabled")
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: {
|
||||
Settings.data.colorSchemes.useWallpaperColors = !Settings.data.colorSchemes.useWallpaperColors;
|
||||
if (Settings.data.colorSchemes.useWallpaperColors) {
|
||||
AppThemeService.generate();
|
||||
} else {
|
||||
ColorSchemeService.setPredefinedScheme(Settings.data.colorSchemes.predefinedScheme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
visible: Settings.data.colorSchemes.useWallpaperColors
|
||||
id: colorSchemeComboBox
|
||||
Layout.fillWidth: false
|
||||
Layout.minimumWidth: 200
|
||||
minimumWidth: 200
|
||||
model: TemplateProcessor.schemeTypes
|
||||
currentKey: Settings.data.colorSchemes.generationMethod
|
||||
|
||||
property bool _initialized: false
|
||||
property bool _userChanging: false
|
||||
Component.onCompleted: Qt.callLater(() => { _initialized = true; })
|
||||
|
||||
model: Settings.data.colorSchemes.useWallpaperColors
|
||||
? TemplateProcessor.schemeTypes
|
||||
: ColorSchemeService.schemes.map(s => ({
|
||||
"key": ColorSchemeService.getBasename(s),
|
||||
"name": ColorSchemeService.getBasename(s)
|
||||
}))
|
||||
currentKey: Settings.data.colorSchemes.useWallpaperColors
|
||||
? Settings.data.colorSchemes.generationMethod
|
||||
: Settings.data.colorSchemes.predefinedScheme
|
||||
onCurrentKeyChanged: {
|
||||
if (!_initialized) return;
|
||||
if (_userChanging) {
|
||||
_userChanging = false;
|
||||
return;
|
||||
}
|
||||
schemeGlowAnimation.restart();
|
||||
}
|
||||
onSelected: key => {
|
||||
Settings.data.colorSchemes.generationMethod = key;
|
||||
AppThemeService.generate();
|
||||
_userChanging = true;
|
||||
if (Settings.data.colorSchemes.useWallpaperColors) {
|
||||
Settings.data.colorSchemes.generationMethod = key;
|
||||
AppThemeService.generate();
|
||||
} else {
|
||||
ColorSchemeService.setPredefinedScheme(key);
|
||||
}
|
||||
Qt.callLater(() => { _userChanging = false; });
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: schemeGlowAnimation
|
||||
NumberAnimation { target: colorSchemeComboBox; property: "opacity"; to: 0.3; duration: Style.animationSlow; easing.type: Easing.OutCubic }
|
||||
NumberAnimation { target: colorSchemeComboBox; property: "opacity"; to: 1.0; duration: Style.animationSlow; easing.type: Easing.InCubic }
|
||||
}
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
@@ -575,12 +631,30 @@ SmartPanel {
|
||||
property var wallpapersWithNames: [] // Cached basenames for files
|
||||
property var directoriesList: [] // List of directories in browse mode
|
||||
|
||||
// ListModel for the grid — enables animated reordering via move()
|
||||
ListModel { id: wallpaperModel }
|
||||
|
||||
// Browse mode properties
|
||||
property string currentBrowsePath: WallpaperService.getCurrentBrowsePath(targetScreen?.name ?? "")
|
||||
property bool isBrowseMode: Settings.data.wallpaper.viewMode === "browse"
|
||||
|
||||
// Expose updateFiltered as a proper function property
|
||||
function updateFiltered() {
|
||||
// Sort favorites to the top (only for non-directory items)
|
||||
function sortFavoritesToTop(items) {
|
||||
var favorited = [];
|
||||
var nonFavorited = [];
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (!items[i].isDirectory && WallpaperService.isFavorite(items[i].path)) {
|
||||
favorited.push(items[i]);
|
||||
} else {
|
||||
nonFavorited.push(items[i]);
|
||||
}
|
||||
}
|
||||
return favorited.concat(nonFavorited);
|
||||
}
|
||||
|
||||
// Rebuild filteredItems and sync to wallpaperModel (full replacement, no animation).
|
||||
// When skipSync is true the caller will animate the model itself.
|
||||
function updateFiltered(skipSync) {
|
||||
var combinedItems = [];
|
||||
|
||||
// In browse mode, add directories first
|
||||
@@ -604,9 +678,12 @@ SmartPanel {
|
||||
});
|
||||
}
|
||||
|
||||
combinedItems = sortFavoritesToTop(combinedItems);
|
||||
|
||||
// Apply filter if text is present
|
||||
if (!panelContent.filterText || panelContent.filterText.trim().length === 0) {
|
||||
filteredItems = combinedItems;
|
||||
if (!skipSync) syncModel();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -614,10 +691,56 @@ SmartPanel {
|
||||
"key": 'name',
|
||||
"limit": 200
|
||||
});
|
||||
// Map back to item list
|
||||
filteredItems = results.map(function (r) {
|
||||
var filtered = results.map(function (r) {
|
||||
return r.obj;
|
||||
});
|
||||
filteredItems = sortFavoritesToTop(filtered);
|
||||
if (!skipSync) syncModel();
|
||||
}
|
||||
|
||||
// Copy filteredItems into the ListModel (full rebuild, no animation).
|
||||
function syncModel() {
|
||||
wallpaperModel.clear();
|
||||
for (var i = 0; i < filteredItems.length; i++) {
|
||||
wallpaperModel.append(filteredItems[i]);
|
||||
}
|
||||
wallpaperGridView.currentIndex = -1;
|
||||
wallpaperGridView.positionViewAtBeginning();
|
||||
}
|
||||
|
||||
// Animate a single item moving to its new position after a favorite toggle.
|
||||
function handleFavoriteMove(path) {
|
||||
// Find where the item currently sits in the model
|
||||
var fromIndex = -1;
|
||||
for (var i = 0; i < wallpaperModel.count; i++) {
|
||||
if (wallpaperModel.get(i).path === path) {
|
||||
fromIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fromIndex === -1) return;
|
||||
|
||||
// Find where it should be in the freshly-computed filteredItems
|
||||
var toIndex = -1;
|
||||
for (var j = 0; j < filteredItems.length; j++) {
|
||||
if (filteredItems[j].path === path) {
|
||||
toIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (toIndex === -1 || fromIndex === toIndex) return;
|
||||
|
||||
wallpaperGridView.animateMovement = true;
|
||||
wallpaperModel.move(fromIndex, toIndex, 1);
|
||||
animateMovementResetTimer.restart();
|
||||
}
|
||||
|
||||
// Turn off move animations shortly after the move completes so that
|
||||
// non-favorite model rebuilds (sort, filter, navigation) don't animate.
|
||||
Timer {
|
||||
id: animateMovementResetTimer
|
||||
interval: Style.animationNormal + 50
|
||||
onTriggered: wallpaperGridView.animateMovement = false
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
@@ -651,6 +774,10 @@ SmartPanel {
|
||||
refreshWallpaperScreenData();
|
||||
}
|
||||
}
|
||||
function onFavoritesChanged(path) {
|
||||
updateFiltered(true); // recompute filteredItems but skip full model rebuild
|
||||
handleFavoriteMove(path); // animate the item to its new position
|
||||
}
|
||||
}
|
||||
|
||||
function refreshWallpaperScreenData() {
|
||||
@@ -680,13 +807,13 @@ SmartPanel {
|
||||
}
|
||||
}
|
||||
|
||||
function selectItem(item) {
|
||||
if (item.isDirectory) {
|
||||
WallpaperService.setBrowsePath(targetScreen.name, item.path);
|
||||
} else if (Settings.data.wallpaper.setWallpaperOnAllMonitors) {
|
||||
WallpaperService.changeWallpaper(item.path, undefined);
|
||||
function selectItem(path, isDirectory) {
|
||||
if (isDirectory) {
|
||||
WallpaperService.setBrowsePath(targetScreen.name, path);
|
||||
} else {
|
||||
WallpaperService.changeWallpaper(item.path, targetScreen.name);
|
||||
var screen = Settings.data.wallpaper.setWallpaperOnAllMonitors ? undefined : targetScreen.name;
|
||||
WallpaperService.changeWallpaper(path, screen);
|
||||
WallpaperService.applyFavoriteTheme(path, screen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -864,13 +991,7 @@ SmartPanel {
|
||||
highlightFollowsCurrentItem: false
|
||||
currentIndex: -1
|
||||
|
||||
model: filteredItems
|
||||
|
||||
onModelChanged: {
|
||||
// Reset selection and scroll position when model changes
|
||||
currentIndex = -1;
|
||||
positionViewAtBeginning();
|
||||
}
|
||||
model: wallpaperModel
|
||||
|
||||
Component.onCompleted: {
|
||||
positionViewAtBeginning();
|
||||
@@ -923,10 +1044,11 @@ SmartPanel {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginXS
|
||||
|
||||
property string wallpaperPath: modelData.path ?? ""
|
||||
property bool isDirectory: modelData.isDirectory ?? false
|
||||
property string wallpaperPath: model.path ?? ""
|
||||
property bool isDirectory: model.isDirectory ?? false
|
||||
property bool isSelected: !isDirectory && (wallpaperPath === currentWallpaper)
|
||||
property string filename: modelData.name ?? wallpaperPath.split('/').pop()
|
||||
property bool isFavorited: !isDirectory && WallpaperService.isFavorite(wallpaperPath)
|
||||
property string filename: model.name ?? wallpaperPath.split('/').pop()
|
||||
property string cachedPath: ""
|
||||
|
||||
spacing: Style.marginXS
|
||||
@@ -1043,6 +1165,106 @@ SmartPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// Favorite star button (top-left)
|
||||
Rectangle {
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Style.marginS
|
||||
width: 28
|
||||
height: 28
|
||||
radius: width / 2
|
||||
visible: !wallpaperItem.isDirectory && (wallpaperItem.isFavorited || hoverHandler.hovered || wallpaperGridView.currentIndex === index)
|
||||
color: {
|
||||
if (wallpaperItem.isFavorited)
|
||||
return starHoverHandler.hovered ? Color.mHover : Color.mPrimary;
|
||||
return starHoverHandler.hovered ? Color.mSurfaceVariant : Color.mSurface;
|
||||
}
|
||||
opacity: wallpaperItem.isFavorited || starHoverHandler.hovered ? 1.0 : 0.7
|
||||
z: 5
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: Style.animationFast }
|
||||
}
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: Style.animationFast }
|
||||
}
|
||||
|
||||
NIcon {
|
||||
icon: wallpaperItem.isFavorited ? "star-filled" : "star"
|
||||
pointSize: Style.fontSizeM
|
||||
color: {
|
||||
if (wallpaperItem.isFavorited)
|
||||
return starHoverHandler.hovered ? Color.mOnHover : Color.mOnPrimary;
|
||||
return starHoverHandler.hovered ? Color.mOnSurface : Color.mOnSurfaceVariant;
|
||||
}
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: starHoverHandler
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onTapped: {
|
||||
WallpaperService.toggleFavorite(wallpaperItem.wallpaperPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Palette color dots (bottom-center, favorites only)
|
||||
Row {
|
||||
id: paletteRow
|
||||
anchors.bottom: img.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottomMargin: Style.marginS
|
||||
spacing: Style.marginXS
|
||||
z: 5
|
||||
visible: wallpaperItem.isFavorited && paletteRow.colors.length > 0
|
||||
|
||||
property int diameter: 25 * Style.uiScaleRatio
|
||||
property int _favRevision: 0
|
||||
property var favData: { _favRevision; return WallpaperService.getFavorite(wallpaperItem.wallpaperPath); }
|
||||
property var colors: favData && favData.paletteColors ? favData.paletteColors : []
|
||||
property bool isDark: favData ? favData.darkMode : false
|
||||
|
||||
Connections {
|
||||
target: WallpaperService
|
||||
function onFavoriteDataUpdated(path) {
|
||||
if (path === wallpaperItem.wallpaperPath) paletteRow._favRevision++;
|
||||
}
|
||||
}
|
||||
|
||||
// Dark/light mode indicator
|
||||
Rectangle {
|
||||
width: paletteRow.diameter
|
||||
height: paletteRow.diameter
|
||||
radius: width * 0.5
|
||||
color: Color.mSurface
|
||||
border.color: Color.mShadow
|
||||
border.width: Style.borderS
|
||||
|
||||
NIcon {
|
||||
icon: paletteRow.isDark ? "moon" : "sun"
|
||||
pointSize: parent.width * 0.45
|
||||
color: Color.mOnSurface
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: paletteRow.colors
|
||||
|
||||
Rectangle {
|
||||
width: paletteRow.diameter
|
||||
height: paletteRow.diameter
|
||||
radius: width * 0.5
|
||||
color: modelData
|
||||
border.color: Color.mShadow
|
||||
border.width: Style.borderS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
@@ -1066,7 +1288,7 @@ SmartPanel {
|
||||
onTapped: {
|
||||
wallpaperGridView.forceActiveFocus();
|
||||
wallpaperGridView.currentIndex = index;
|
||||
selectItem(modelData);
|
||||
selectItem(wallpaperItem.wallpaperPath, wallpaperItem.isDirectory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1093,7 +1315,7 @@ SmartPanel {
|
||||
radius: Style.radiusM
|
||||
border.color: Color.mOutline
|
||||
border.width: Style.borderS
|
||||
visible: (filteredItems.length === 0 && !WallpaperService.scanning) || WallpaperService.scanning
|
||||
visible: (wallpaperModel.count === 0 && !WallpaperService.scanning) || WallpaperService.scanning
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 130
|
||||
|
||||
@@ -1107,7 +1329,7 @@ SmartPanel {
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
visible: filteredItems.length === 0 && !WallpaperService.scanning
|
||||
visible: wallpaperModel.count === 0 && !WallpaperService.scanning
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Commons
|
||||
import qs.Services.Theming
|
||||
import qs.Services.UI
|
||||
|
||||
Singleton {
|
||||
@@ -345,11 +346,14 @@ Singleton {
|
||||
Settings.data.wallpaper.useSolidColor = false;
|
||||
}
|
||||
|
||||
// Save current favorite color schemes before switching away.
|
||||
// This must happen before applyFavoriteTheme (called by the UI)
|
||||
// overwrites the settings that _createFavoriteEntry reads.
|
||||
_saveOutgoingFavorites(path, screenName);
|
||||
|
||||
if (screenName !== undefined) {
|
||||
_setWallpaper(screenName, path);
|
||||
} else {
|
||||
// If no screenName specified change for all screens
|
||||
// Merge connected screens and cached screens to include disconnected monitors
|
||||
var allScreenNames = new Set(Object.keys(currentWallpapers));
|
||||
for (var i = 0; i < Quickshell.screens.length; i++) {
|
||||
allScreenNames.add(Quickshell.screens[i].name);
|
||||
@@ -358,6 +362,23 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Save the color scheme of any favorited wallpapers that are about
|
||||
// to be replaced, while the current settings still reflect them.
|
||||
function _saveOutgoingFavorites(newPath, screenName) {
|
||||
var outgoing = screenName !== undefined
|
||||
? [currentWallpapers[screenName]]
|
||||
: Object.values(currentWallpapers);
|
||||
|
||||
var unique = [...new Set(outgoing)];
|
||||
|
||||
unique.forEach(function(path) {
|
||||
if (path && path !== newPath && isFavorite(path)) {
|
||||
updateFavoriteColorScheme(path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
function _setWallpaper(screenName, path) {
|
||||
if (path === "" || path === undefined) {
|
||||
@@ -369,14 +390,8 @@ Singleton {
|
||||
return;
|
||||
}
|
||||
|
||||
//Logger.i("Wallpaper", "setWallpaper on", screenName, ": ", path)
|
||||
|
||||
// Check if wallpaper actually changed
|
||||
var oldPath = currentWallpapers[screenName] || "";
|
||||
var wallpaperChanged = (oldPath !== path);
|
||||
|
||||
if (!wallpaperChanged) {
|
||||
// No change needed
|
||||
if (oldPath === path) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -903,6 +918,129 @@ Singleton {
|
||||
_scanDirectoryInternal(screenName, directory, true, true, null);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Favorites
|
||||
// -------------------------------------------------------------------
|
||||
readonly property int _favoriteNotFound: -1
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
function _findFavoriteIndex(path) {
|
||||
var favorites = Settings.data.wallpaper.favorites;
|
||||
for (var i = 0; i < favorites.length; i++) {
|
||||
if (favorites[i].path === path) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return _favoriteNotFound;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
function _createFavoriteEntry(path) {
|
||||
return {
|
||||
"path": path,
|
||||
"colorScheme": Settings.data.colorSchemes.predefinedScheme,
|
||||
"darkMode": Settings.data.colorSchemes.darkMode,
|
||||
"useWallpaperColors": Settings.data.colorSchemes.useWallpaperColors,
|
||||
"generationMethod": Settings.data.colorSchemes.generationMethod,
|
||||
"paletteColors": [
|
||||
Color.mPrimary.toString(),
|
||||
Color.mSecondary.toString(),
|
||||
Color.mTertiary.toString(),
|
||||
Color.mError.toString()
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
function isFavorite(path) {
|
||||
return _findFavoriteIndex(path) !== _favoriteNotFound;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
function getFavorite(path) {
|
||||
var favoriteIndex = _findFavoriteIndex(path);
|
||||
if (favoriteIndex === _favoriteNotFound)
|
||||
return null;
|
||||
return Settings.data.wallpaper.favorites[favoriteIndex];
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
function toggleFavorite(path) {
|
||||
var favorites = Settings.data.wallpaper.favorites.slice();
|
||||
var existingIndex = _findFavoriteIndex(path);
|
||||
|
||||
if (existingIndex !== _favoriteNotFound) {
|
||||
favorites.splice(existingIndex, 1);
|
||||
Logger.d("Wallpaper", "Removed favorite:", path);
|
||||
} else {
|
||||
favorites.push(_createFavoriteEntry(path));
|
||||
Logger.d("Wallpaper", "Added favorite:", path);
|
||||
}
|
||||
|
||||
Settings.data.wallpaper.favorites = favorites;
|
||||
favoritesChanged(path);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
function applyFavoriteTheme(path, screenName) {
|
||||
// Only apply theme if the wallpaper is on the monitor driving colors
|
||||
var effectiveMonitor = Settings.data.colorSchemes.monitorForColors;
|
||||
if (effectiveMonitor === "" || effectiveMonitor === undefined) {
|
||||
effectiveMonitor = Quickshell.screens.length > 0 ? Quickshell.screens[0].name : "";
|
||||
}
|
||||
if (screenName !== undefined && screenName !== effectiveMonitor) {
|
||||
return;
|
||||
}
|
||||
|
||||
var favorite = getFavorite(path);
|
||||
if (!favorite)
|
||||
return;
|
||||
|
||||
// Only update settings — generation is handled by the wallpaperChanged
|
||||
// signal in AppThemeService, which fires after changeWallpaper().
|
||||
Settings.data.colorSchemes.useWallpaperColors = favorite.useWallpaperColors;
|
||||
Settings.data.colorSchemes.predefinedScheme = favorite.colorScheme;
|
||||
Settings.data.colorSchemes.generationMethod = favorite.generationMethod;
|
||||
Settings.data.colorSchemes.darkMode = favorite.darkMode;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
function updateFavoriteColorScheme(path) {
|
||||
var existingIndex = _findFavoriteIndex(path);
|
||||
if (existingIndex === _favoriteNotFound)
|
||||
return;
|
||||
|
||||
var favorites = Settings.data.wallpaper.favorites.slice();
|
||||
favorites[existingIndex] = _createFavoriteEntry(path);
|
||||
Settings.data.wallpaper.favorites = favorites;
|
||||
Logger.d("Wallpaper", "Updated color scheme for favorite:", path);
|
||||
favoriteDataUpdated(path);
|
||||
}
|
||||
|
||||
signal favoritesChanged(string path)
|
||||
signal favoriteDataUpdated(string path)
|
||||
|
||||
// Auto-update favorite palette colors when theme colors finish transitioning
|
||||
Connections {
|
||||
target: Color
|
||||
function onIsTransitioningChanged() {
|
||||
if (!Color.isTransitioning) {
|
||||
_updateCurrentWallpaperFavorites();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _updateCurrentWallpaperFavorites() {
|
||||
var effectiveMonitor = Settings.data.colorSchemes.monitorForColors;
|
||||
if (effectiveMonitor === "" || effectiveMonitor === undefined) {
|
||||
effectiveMonitor = Quickshell.screens.length > 0 ? Quickshell.screens[0].name : "";
|
||||
}
|
||||
var wp = getWallpaper(effectiveMonitor);
|
||||
if (wp && isFavorite(wp)) {
|
||||
updateFavoriteColorScheme(wp);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
@@ -77,6 +77,9 @@ Item {
|
||||
property alias verticalVelocity: gridView.verticalVelocity
|
||||
property alias reuseItems: gridView.reuseItems
|
||||
|
||||
// Animate items when the model is reordered (e.g. ListModel.move())
|
||||
property bool animateMovement: false
|
||||
|
||||
// Scroll speed multiplier for mouse wheel (1.0 = default, higher = faster)
|
||||
property real wheelScrollMultiplier: 2.0
|
||||
|
||||
@@ -248,6 +251,18 @@ Item {
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: root.reserveScrollbarSpace ? root.handleWidth + Style.marginXS : 0
|
||||
|
||||
move: root.animateMovement ? moveTransitionImpl : null
|
||||
displaced: root.animateMovement ? displacedTransitionImpl : null
|
||||
|
||||
Transition {
|
||||
id: moveTransitionImpl
|
||||
NumberAnimation { properties: "x,y"; duration: Style.animationNormal; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
Transition {
|
||||
id: displacedTransitionImpl
|
||||
NumberAnimation { properties: "x,y"; duration: Style.animationNormal; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
|
||||
// Enable clipping to keep content within bounds
|
||||
clip: true
|
||||
|
||||
|
||||
Reference in New Issue
Block a user