mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-07 05:55:37 -05:00
launcher: sort by usage frequency
This commit is contained in:
@@ -15,7 +15,7 @@ Singleton {
|
|||||||
property bool isLightMode: false
|
property bool isLightMode: false
|
||||||
property real topBarTransparency: 0.75
|
property real topBarTransparency: 0.75
|
||||||
property real popupTransparency: 0.92
|
property real popupTransparency: 0.92
|
||||||
property var recentlyUsedApps: []
|
property var appUsageRanking: {}
|
||||||
property bool use24HourClock: true
|
property bool use24HourClock: true
|
||||||
property bool useFahrenheit: false
|
property bool useFahrenheit: false
|
||||||
property bool nightModeEnabled: false
|
property bool nightModeEnabled: false
|
||||||
@@ -58,7 +58,7 @@ Singleton {
|
|||||||
isLightMode = settings.isLightMode !== undefined ? settings.isLightMode : false;
|
isLightMode = settings.isLightMode !== undefined ? settings.isLightMode : false;
|
||||||
topBarTransparency = settings.topBarTransparency !== undefined ? (settings.topBarTransparency > 1 ? settings.topBarTransparency / 100 : settings.topBarTransparency) : 0.75;
|
topBarTransparency = settings.topBarTransparency !== undefined ? (settings.topBarTransparency > 1 ? settings.topBarTransparency / 100 : settings.topBarTransparency) : 0.75;
|
||||||
popupTransparency = settings.popupTransparency !== undefined ? (settings.popupTransparency > 1 ? settings.popupTransparency / 100 : settings.popupTransparency) : 0.92;
|
popupTransparency = settings.popupTransparency !== undefined ? (settings.popupTransparency > 1 ? settings.popupTransparency / 100 : settings.popupTransparency) : 0.92;
|
||||||
recentlyUsedApps = settings.recentlyUsedApps || [];
|
appUsageRanking = settings.appUsageRanking || {};
|
||||||
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true;
|
use24HourClock = settings.use24HourClock !== undefined ? settings.use24HourClock : true;
|
||||||
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false;
|
useFahrenheit = settings.useFahrenheit !== undefined ? settings.useFahrenheit : false;
|
||||||
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false;
|
nightModeEnabled = settings.nightModeEnabled !== undefined ? settings.nightModeEnabled : false;
|
||||||
@@ -104,7 +104,7 @@ Singleton {
|
|||||||
"isLightMode": isLightMode,
|
"isLightMode": isLightMode,
|
||||||
"topBarTransparency": topBarTransparency,
|
"topBarTransparency": topBarTransparency,
|
||||||
"popupTransparency": popupTransparency,
|
"popupTransparency": popupTransparency,
|
||||||
"recentlyUsedApps": recentlyUsedApps,
|
"appUsageRanking": appUsageRanking,
|
||||||
"use24HourClock": use24HourClock,
|
"use24HourClock": use24HourClock,
|
||||||
"useFahrenheit": useFahrenheit,
|
"useFahrenheit": useFahrenheit,
|
||||||
"nightModeEnabled": nightModeEnabled,
|
"nightModeEnabled": nightModeEnabled,
|
||||||
@@ -179,56 +179,77 @@ Singleton {
|
|||||||
saveSettings();
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addRecentApp(app) {
|
function addAppUsage(app) {
|
||||||
if (!app)
|
if (!app)
|
||||||
return ;
|
return;
|
||||||
|
|
||||||
var execProp = app.execString || app.exec || "";
|
var appId = app.id || (app.execString || app.exec || "");
|
||||||
if (!execProp)
|
if (!appId)
|
||||||
return ;
|
return;
|
||||||
|
|
||||||
var existingIndex = -1;
|
var currentRanking = Object.assign({}, appUsageRanking);
|
||||||
for (var i = 0; i < recentlyUsedApps.length; i++) {
|
|
||||||
if (recentlyUsedApps[i].exec === execProp) {
|
if (currentRanking[appId]) {
|
||||||
existingIndex = i;
|
currentRanking[appId].usageCount = (currentRanking[appId].usageCount || 1) + 1;
|
||||||
break;
|
currentRanking[appId].lastUsed = Date.now();
|
||||||
}
|
currentRanking[appId].icon = app.icon || currentRanking[appId].icon || "application-x-executable";
|
||||||
}
|
currentRanking[appId].name = app.name || currentRanking[appId].name || "";
|
||||||
if (existingIndex >= 0) {
|
|
||||||
// App exists, increment usage count
|
|
||||||
recentlyUsedApps[existingIndex].usageCount = (recentlyUsedApps[existingIndex].usageCount || 1) + 1;
|
|
||||||
recentlyUsedApps[existingIndex].lastUsed = Date.now();
|
|
||||||
} else {
|
} else {
|
||||||
// New app, create entry
|
currentRanking[appId] = {
|
||||||
var appData = {
|
|
||||||
"name": app.name || "",
|
"name": app.name || "",
|
||||||
"exec": execProp,
|
"exec": app.execString || app.exec || "",
|
||||||
"icon": app.icon || "application-x-executable",
|
"icon": app.icon || "application-x-executable",
|
||||||
"comment": app.comment || "",
|
"comment": app.comment || "",
|
||||||
"usageCount": 1,
|
"usageCount": 1,
|
||||||
"lastUsed": Date.now()
|
"lastUsed": Date.now()
|
||||||
};
|
};
|
||||||
recentlyUsedApps.push(appData);
|
|
||||||
}
|
}
|
||||||
// Sort by usage count (descending), then alphabetically by name
|
|
||||||
var sortedApps = recentlyUsedApps.sort(function(a, b) {
|
|
||||||
if (a.usageCount !== b.usageCount)
|
|
||||||
return b.usageCount - a.usageCount;
|
|
||||||
|
|
||||||
// Higher usage count first
|
appUsageRanking = currentRanking;
|
||||||
return a.name.localeCompare(b.name);
|
|
||||||
});
|
|
||||||
// Limit to 10 apps
|
|
||||||
if (sortedApps.length > 10)
|
|
||||||
sortedApps = sortedApps.slice(0, 10);
|
|
||||||
|
|
||||||
// Reassign to trigger property change signal
|
|
||||||
recentlyUsedApps = sortedApps;
|
|
||||||
saveSettings();
|
saveSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRecentApps() {
|
function getAppUsageRanking() {
|
||||||
return recentlyUsedApps;
|
return appUsageRanking;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRankedApps() {
|
||||||
|
var apps = [];
|
||||||
|
for (var appId in appUsageRanking) {
|
||||||
|
var appData = appUsageRanking[appId];
|
||||||
|
apps.push({
|
||||||
|
id: appId,
|
||||||
|
name: appData.name,
|
||||||
|
exec: appData.exec,
|
||||||
|
icon: appData.icon,
|
||||||
|
comment: appData.comment,
|
||||||
|
usageCount: appData.usageCount,
|
||||||
|
lastUsed: appData.lastUsed
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return apps.sort(function(a, b) {
|
||||||
|
if (a.usageCount !== b.usageCount)
|
||||||
|
return b.usageCount - a.usageCount;
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanupAppUsageRanking(availableAppIds) {
|
||||||
|
var currentRanking = Object.assign({}, appUsageRanking);
|
||||||
|
var hasChanges = false;
|
||||||
|
|
||||||
|
for (var appId in currentRanking) {
|
||||||
|
if (availableAppIds.indexOf(appId) === -1) {
|
||||||
|
delete currentRanking[appId];
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasChanges) {
|
||||||
|
appUsageRanking = currentRanking;
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// New preference setters
|
// New preference setters
|
||||||
|
|||||||
@@ -550,8 +550,8 @@ DankModal {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: Theme.spacingM
|
anchors.rightMargin: Theme.spacingM
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
iconName: "dangerous"
|
iconName: "close"
|
||||||
iconSize: Theme.iconSize - 4
|
iconSize: Theme.iconSize - 6
|
||||||
iconColor: Theme.error
|
iconColor: Theme.error
|
||||||
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
// DankModal configuration
|
// DankModal configuration
|
||||||
visible: settingsVisible
|
visible: settingsVisible
|
||||||
width: 650
|
width: 750
|
||||||
height: 750
|
height: 750
|
||||||
keyboardFocus: "ondemand"
|
keyboardFocus: "ondemand"
|
||||||
onBackgroundClicked: {
|
onBackgroundClicked: {
|
||||||
@@ -92,6 +92,7 @@ DankModal {
|
|||||||
{ text: "Personalization", icon: "person" },
|
{ text: "Personalization", icon: "person" },
|
||||||
{ text: "Time & Weather", icon: "schedule" },
|
{ text: "Time & Weather", icon: "schedule" },
|
||||||
{ text: "Widgets", icon: "widgets" },
|
{ text: "Widgets", icon: "widgets" },
|
||||||
|
{ text: "Launcher", icon: "apps" },
|
||||||
{ text: "Appearance", icon: "palette" }
|
{ text: "Appearance", icon: "palette" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -128,7 +129,7 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// System Tab
|
// Widgets Tab
|
||||||
Loader {
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: settingsTabBar.currentIndex === 2
|
active: settingsTabBar.currentIndex === 2
|
||||||
@@ -139,12 +140,23 @@ DankModal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Appearance Tab
|
// Launcher Tab
|
||||||
Loader {
|
Loader {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
active: settingsTabBar.currentIndex === 3
|
active: settingsTabBar.currentIndex === 3
|
||||||
visible: active
|
visible: active
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
sourceComponent: Component {
|
||||||
|
LauncherTab {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appearance Tab
|
||||||
|
Loader {
|
||||||
|
anchors.fill: parent
|
||||||
|
active: settingsTabBar.currentIndex === 4
|
||||||
|
visible: active
|
||||||
|
asynchronous: true
|
||||||
sourceComponent: Component {
|
sourceComponent: Component {
|
||||||
AppearanceTab {}
|
AppearanceTab {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ DankModal {
|
|||||||
|
|
||||||
content: Component {
|
content: Component {
|
||||||
Item {
|
Item {
|
||||||
|
id: spotlightKeyHandler
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
// Handle keyboard shortcuts
|
// Handle keyboard shortcuts
|
||||||
@@ -115,9 +116,9 @@ DankModal {
|
|||||||
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||||
appLauncher.launchSelected();
|
appLauncher.launchSelected();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
} else if (event.text && event.text.length > 0 && event.text.match(/[a-zA-Z0-9\\s]/)) {
|
} else if (!searchField.activeFocus && event.text && event.text.length > 0 && event.text.match(/[a-zA-Z0-9\\s]/)) {
|
||||||
searchField.text = event.text;
|
|
||||||
searchField.forceActiveFocus();
|
searchField.forceActiveFocus();
|
||||||
|
searchField.insertText(event.text);
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,6 +163,8 @@ DankModal {
|
|||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
enabled: spotlightOpen
|
enabled: spotlightOpen
|
||||||
placeholderText: "Search applications..."
|
placeholderText: "Search applications..."
|
||||||
|
ignoreLeftRightKeys: true
|
||||||
|
keyForwardTargets: [spotlightKeyHandler]
|
||||||
text: appLauncher.searchQuery
|
text: appLauncher.searchQuery
|
||||||
onTextEdited: {
|
onTextEdited: {
|
||||||
appLauncher.searchQuery = text;
|
appLauncher.searchQuery = text;
|
||||||
@@ -170,14 +173,14 @@ DankModal {
|
|||||||
if (event.key === Qt.Key_Escape) {
|
if (event.key === Qt.Key_Escape) {
|
||||||
hide();
|
hide();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
} else if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && appLauncher.searchQuery.length > 0) {
|
} else if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length > 0) {
|
||||||
// Launch first app when typing in search field
|
if (appLauncher.keyboardNavigationActive && appLauncher.model.count > 0) {
|
||||||
if (appLauncher.model.count > 0)
|
appLauncher.launchSelected();
|
||||||
|
} else if (appLauncher.model.count > 0) {
|
||||||
appLauncher.launchApp(appLauncher.model.get(0));
|
appLauncher.launchApp(appLauncher.model.get(0));
|
||||||
|
}
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up || (event.key === Qt.Key_Left && appLauncher.viewMode === "grid") || (event.key === Qt.Key_Right && appLauncher.viewMode === "grid") || ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && appLauncher.searchQuery.length === 0)) {
|
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up || event.key === Qt.Key_Left || event.key === Qt.Key_Right || ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length === 0)) {
|
||||||
// Pass navigation keys and enter (when not searching) to main handler
|
|
||||||
event.accepted = false;
|
event.accepted = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ PanelWindow {
|
|||||||
|
|
||||||
// Content with focus management
|
// Content with focus management
|
||||||
Item {
|
Item {
|
||||||
|
id: keyHandler
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
focus: true
|
focus: true
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
@@ -172,10 +173,10 @@ PanelWindow {
|
|||||||
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
} else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||||
appLauncher.launchSelected();
|
appLauncher.launchSelected();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
} else if (event.text && event.text.length > 0 && event.text.match(/[a-zA-Z0-9\\s]/)) {
|
} else if (!searchField.activeFocus && event.text && event.text.length > 0 && event.text.match(/[a-zA-Z0-9\\s]/)) {
|
||||||
// User started typing, focus search field and pass the character
|
// User started typing, focus search field and pass the character
|
||||||
searchField.forceActiveFocus();
|
searchField.forceActiveFocus();
|
||||||
searchField.text = event.text;
|
searchField.insertText(event.text);
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,17 +234,21 @@ PanelWindow {
|
|||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
enabled: appDrawerPopout.isVisible
|
enabled: appDrawerPopout.isVisible
|
||||||
placeholderText: "Search applications..."
|
placeholderText: "Search applications..."
|
||||||
|
ignoreLeftRightKeys: true
|
||||||
|
keyForwardTargets: [keyHandler]
|
||||||
onTextEdited: {
|
onTextEdited: {
|
||||||
appLauncher.searchQuery = text;
|
appLauncher.searchQuery = text;
|
||||||
}
|
}
|
||||||
Keys.onPressed: function(event) {
|
Keys.onPressed: function(event) {
|
||||||
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && appLauncher.model.count && text.length > 0) {
|
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length > 0) {
|
||||||
// Launch first app when typing in search field
|
if (appLauncher.keyboardNavigationActive && appLauncher.model.count > 0) {
|
||||||
|
appLauncher.launchSelected();
|
||||||
|
} else if (appLauncher.model.count > 0) {
|
||||||
var firstApp = appLauncher.model.get(0);
|
var firstApp = appLauncher.model.get(0);
|
||||||
appLauncher.launchApp(firstApp);
|
appLauncher.launchApp(firstApp);
|
||||||
|
}
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up || (event.key === Qt.Key_Left && appLauncher.viewMode === "grid") || (event.key === Qt.Key_Right && appLauncher.viewMode === "grid") || ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length === 0)) {
|
} else if (event.key === Qt.Key_Down || event.key === Qt.Key_Up || event.key === Qt.Key_Left || event.key === Qt.Key_Right || ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length === 0)) {
|
||||||
// Pass navigation keys and enter (when not searching) to main handler
|
|
||||||
event.accepted = false;
|
event.accepted = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ Item {
|
|||||||
var allCategories = AppSearchService.getAllCategories().filter(cat => {
|
var allCategories = AppSearchService.getAllCategories().filter(cat => {
|
||||||
return cat !== "Education" && cat !== "Science";
|
return cat !== "Education" && cat !== "Science";
|
||||||
});
|
});
|
||||||
var result = ["All", "Recents"];
|
var result = ["All"];
|
||||||
return result.concat(allCategories.filter(cat => {
|
return result.concat(allCategories.filter(cat => {
|
||||||
return cat !== "All";
|
return cat !== "All";
|
||||||
}));
|
}));
|
||||||
@@ -33,13 +33,8 @@ Item {
|
|||||||
// Category icons (computed from AppSearchService)
|
// Category icons (computed from AppSearchService)
|
||||||
property var categoryIcons: categories.map(category => AppSearchService.getCategoryIcon(category))
|
property var categoryIcons: categories.map(category => AppSearchService.getCategoryIcon(category))
|
||||||
|
|
||||||
// Recent apps helper
|
// App usage ranking helper
|
||||||
property var recentApps: Prefs.recentlyUsedApps.map(recentApp => {
|
property var appUsageRanking: Prefs.appUsageRanking
|
||||||
var app = AppSearchService.getAppByExec(recentApp.exec);
|
|
||||||
return app && !app.noDisplay ? app : null;
|
|
||||||
}).filter(app => {
|
|
||||||
return app !== null;
|
|
||||||
})
|
|
||||||
|
|
||||||
// Signals
|
// Signals
|
||||||
signal appLaunched(var app)
|
signal appLaunched(var app)
|
||||||
@@ -70,10 +65,12 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onSelectedCategoryChanged: updateFilteredModel()
|
onSelectedCategoryChanged: updateFilteredModel()
|
||||||
|
onAppUsageRankingChanged: updateFilteredModel()
|
||||||
|
|
||||||
function updateFilteredModel() {
|
function updateFilteredModel() {
|
||||||
filteredModel.clear();
|
filteredModel.clear();
|
||||||
selectedIndex = 0;
|
selectedIndex = 0;
|
||||||
|
keyboardNavigationActive = false;
|
||||||
|
|
||||||
var apps = [];
|
var apps = [];
|
||||||
|
|
||||||
@@ -81,8 +78,6 @@ Item {
|
|||||||
// Show apps from category
|
// Show apps from category
|
||||||
if (selectedCategory === "All") {
|
if (selectedCategory === "All") {
|
||||||
apps = AppSearchService.applications || [];
|
apps = AppSearchService.applications || [];
|
||||||
} else if (selectedCategory === "Recents") {
|
|
||||||
apps = recentApps;
|
|
||||||
} else {
|
} else {
|
||||||
var categoryApps = AppSearchService.getAppsInCategory(selectedCategory);
|
var categoryApps = AppSearchService.getAppsInCategory(selectedCategory);
|
||||||
apps = categoryApps.slice(0, maxResults);
|
apps = categoryApps.slice(0, maxResults);
|
||||||
@@ -91,16 +86,6 @@ Item {
|
|||||||
// Search with category filter
|
// Search with category filter
|
||||||
if (selectedCategory === "All") {
|
if (selectedCategory === "All") {
|
||||||
apps = AppSearchService.searchApplications(searchQuery);
|
apps = AppSearchService.searchApplications(searchQuery);
|
||||||
} else if (selectedCategory === "Recents") {
|
|
||||||
if (recentApps.length > 0) {
|
|
||||||
var allSearchResults = AppSearchService.searchApplications(searchQuery);
|
|
||||||
var recentNames = new Set(recentApps.map(app => app.name));
|
|
||||||
apps = allSearchResults.filter(searchApp => {
|
|
||||||
return recentNames.has(searchApp.name);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
apps = [];
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
var categoryApps = AppSearchService.getAppsInCategory(selectedCategory);
|
var categoryApps = AppSearchService.getAppsInCategory(selectedCategory);
|
||||||
if (categoryApps.length > 0) {
|
if (categoryApps.length > 0) {
|
||||||
@@ -115,6 +100,21 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort apps by usage ranking, then alphabetically
|
||||||
|
apps = apps.sort(function(a, b) {
|
||||||
|
var aId = a.id || (a.execString || a.exec || "");
|
||||||
|
var bId = b.id || (b.execString || b.exec || "");
|
||||||
|
|
||||||
|
var aUsage = appUsageRanking[aId] ? appUsageRanking[aId].usageCount : 0;
|
||||||
|
var bUsage = appUsageRanking[bId] ? appUsageRanking[bId].usageCount : 0;
|
||||||
|
|
||||||
|
if (aUsage !== bUsage) {
|
||||||
|
return bUsage - aUsage; // Higher usage first
|
||||||
|
}
|
||||||
|
|
||||||
|
return (a.name || "").localeCompare(b.name || ""); // Alphabetical fallback
|
||||||
|
});
|
||||||
|
|
||||||
// Convert to model format and populate
|
// Convert to model format and populate
|
||||||
apps.forEach(app => {
|
apps.forEach(app => {
|
||||||
if (app) {
|
if (app) {
|
||||||
@@ -178,16 +178,14 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function launchApp(appData) {
|
function launchApp(appData) {
|
||||||
if (appData.desktopEntry) {
|
if (!appData) {
|
||||||
Prefs.addRecentApp(appData.desktopEntry);
|
console.warn("AppLauncher: No app data provided");
|
||||||
appData.desktopEntry.execute();
|
return;
|
||||||
} else {
|
|
||||||
// Fallback to direct execution
|
|
||||||
var cleanExec = appData.exec.replace(/%[fFuU]/g, "").trim();
|
|
||||||
console.log("AppLauncher: Launching app directly:", cleanExec);
|
|
||||||
Quickshell.execDetached(["sh", "-c", cleanExec]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appData.desktopEntry.execute();
|
||||||
appLaunched(appData);
|
appLaunched(appData);
|
||||||
|
Prefs.addAppUsage(appData.desktopEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Category management
|
// Category management
|
||||||
|
|||||||
@@ -58,9 +58,9 @@ Item {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
// Top row: All, Recents, Development, Graphics (4 items)
|
// Top row: All, Development, Graphics, Games (4 items)
|
||||||
Row {
|
Row {
|
||||||
property var topRowCategories: ["All", "Recents", "Development", "Graphics"]
|
property var topRowCategories: ["All", "Development", "Graphics", "Games"]
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingS
|
spacing: Theme.spacingS
|
||||||
|
|||||||
@@ -598,13 +598,13 @@ Item {
|
|||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingL
|
||||||
width: Math.min(600, parent.width - Theme.spacingXL * 2)
|
width: Math.min(parent.width - Theme.spacingXL * 2, implicitWidth)
|
||||||
text: randomFact
|
text: randomFact
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: "white"
|
color: "white"
|
||||||
opacity: 0.8
|
opacity: 0.8
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.NoWrap
|
||||||
visible: randomFact !== ""
|
visible: randomFact !== ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
399
Modules/Settings/LauncherTab.qml
Normal file
399
Modules/Settings/LauncherTab.qml
Normal file
@@ -0,0 +1,399 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import qs.Common
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
id: launcherTab
|
||||||
|
|
||||||
|
contentWidth: availableWidth
|
||||||
|
contentHeight: column.implicitHeight + Theme.spacingXL
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: column
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXL
|
||||||
|
topPadding: Theme.spacingL
|
||||||
|
bottomPadding: Theme.spacingXL
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: parent.width
|
||||||
|
height: appLauncherSection.implicitHeight + Theme.spacingL * 2
|
||||||
|
radius: Theme.cornerRadiusLarge
|
||||||
|
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: appLauncherSection
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingL
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "apps"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "App Launcher"
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
width: parent.width
|
||||||
|
text: "Use OS Logo for App Launcher"
|
||||||
|
description: "Display operating system logo instead of apps icon"
|
||||||
|
checked: Prefs.useOSLogo
|
||||||
|
onToggled: (checked) => {
|
||||||
|
return Prefs.setUseOSLogo(checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: parent.width
|
||||||
|
height: logoCustomization.implicitHeight + Theme.spacingM * 2
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
|
||||||
|
border.width: 1
|
||||||
|
visible: Prefs.useOSLogo
|
||||||
|
opacity: visible ? 1 : 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: logoCustomization
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "OS Logo Customization"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Color Override"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
width: 160
|
||||||
|
height: 36
|
||||||
|
placeholderText: "#ffffff"
|
||||||
|
text: Prefs.osLogoColorOverride
|
||||||
|
maximumLength: 7
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
topPadding: Theme.spacingS
|
||||||
|
bottomPadding: Theme.spacingS
|
||||||
|
onEditingFinished: {
|
||||||
|
var color = text.trim();
|
||||||
|
if (color === "" || /^#[0-9A-Fa-f]{6}$/.test(color))
|
||||||
|
Prefs.setOSLogoColorOverride(color);
|
||||||
|
else
|
||||||
|
text = Prefs.osLogoColorOverride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Brightness"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
DankSlider {
|
||||||
|
width: parent.width
|
||||||
|
height: 24
|
||||||
|
minimum: 0
|
||||||
|
maximum: 100
|
||||||
|
value: Math.round(Prefs.osLogoBrightness * 100)
|
||||||
|
unit: ""
|
||||||
|
showValue: true
|
||||||
|
onSliderValueChanged: (newValue) => {
|
||||||
|
Prefs.setOSLogoBrightness(newValue / 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Contrast"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
DankSlider {
|
||||||
|
width: parent.width
|
||||||
|
height: 24
|
||||||
|
minimum: 0
|
||||||
|
maximum: 200
|
||||||
|
value: Math.round(Prefs.osLogoContrast * 100)
|
||||||
|
unit: ""
|
||||||
|
showValue: true
|
||||||
|
onSliderValueChanged: (newValue) => {
|
||||||
|
Prefs.setOSLogoContrast(newValue / 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
width: parent.width
|
||||||
|
height: recentlyUsedSection.implicitHeight + Theme.spacingL * 2
|
||||||
|
radius: Theme.cornerRadiusLarge
|
||||||
|
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: recentlyUsedSection
|
||||||
|
|
||||||
|
property var rankedAppsModel: {
|
||||||
|
var apps = [];
|
||||||
|
for (var appId in Prefs.appUsageRanking) {
|
||||||
|
var appData = Prefs.appUsageRanking[appId];
|
||||||
|
apps.push({
|
||||||
|
"id": appId,
|
||||||
|
"name": appData.name,
|
||||||
|
"exec": appData.exec,
|
||||||
|
"icon": appData.icon,
|
||||||
|
"comment": appData.comment,
|
||||||
|
"usageCount": appData.usageCount,
|
||||||
|
"lastUsed": appData.lastUsed
|
||||||
|
});
|
||||||
|
}
|
||||||
|
apps.sort(function(a, b) {
|
||||||
|
if (a.usageCount !== b.usageCount)
|
||||||
|
return b.usageCount - a.usageCount;
|
||||||
|
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
});
|
||||||
|
return apps.slice(0, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingL
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "history"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Recently Used Apps"
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width - parent.children[0].width - parent.children[1].width - clearAllButton.width - Theme.spacingM * 3
|
||||||
|
height: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
id: clearAllButton
|
||||||
|
|
||||||
|
iconName: "delete_sweep"
|
||||||
|
iconSize: Theme.iconSize - 2
|
||||||
|
iconColor: Theme.error
|
||||||
|
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onClicked: {
|
||||||
|
Prefs.appUsageRanking = {
|
||||||
|
};
|
||||||
|
Prefs.saveSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width
|
||||||
|
text: "Apps are ordered by usage frequency, then alphabetically."
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: rankedAppsList
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: recentlyUsedSection.rankedAppsModel
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
width: rankedAppsList.width
|
||||||
|
height: 48
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.3)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: (index + 1).toString()
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.primary
|
||||||
|
width: 20
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
source: modelData.icon ? "image://icon/" + modelData.icon : "image://icon/application-x-executable"
|
||||||
|
sourceSize.width: 24
|
||||||
|
sourceSize.height: 24
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === Image.Error)
|
||||||
|
source = "image://icon/application-x-executable";
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.name || "Unknown App"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (!modelData.lastUsed) return "Never used";
|
||||||
|
var date = new Date(modelData.lastUsed);
|
||||||
|
var now = new Date();
|
||||||
|
var diffMs = now - date;
|
||||||
|
var diffMins = Math.floor(diffMs / (1000 * 60));
|
||||||
|
var diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
||||||
|
var diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
|
if (diffMins < 1) return "Last launched just now";
|
||||||
|
if (diffMins < 60) return "Last launched " + diffMins + " minute" + (diffMins === 1 ? "" : "s") + " ago";
|
||||||
|
if (diffHours < 24) return "Last launched " + diffHours + " hour" + (diffHours === 1 ? "" : "s") + " ago";
|
||||||
|
if (diffDays < 7) return "Last launched " + diffDays + " day" + (diffDays === 1 ? "" : "s") + " ago";
|
||||||
|
|
||||||
|
return "Last launched " + date.toLocaleDateString();
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
circular: true
|
||||||
|
iconName: "close"
|
||||||
|
iconSize: 16
|
||||||
|
iconColor: Theme.error
|
||||||
|
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||||
|
onClicked: {
|
||||||
|
var currentRanking = Object.assign({}, Prefs.appUsageRanking);
|
||||||
|
delete currentRanking[modelData.id];
|
||||||
|
Prefs.appUsageRanking = currentRanking;
|
||||||
|
Prefs.saveSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
width: parent.width
|
||||||
|
text: recentlyUsedSection.rankedAppsModel.length === 0 ? "No apps have been launched yet." : ""
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
visible: recentlyUsedSection.rankedAppsModel.length === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -170,165 +170,5 @@ ScrollView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// App Launcher Section
|
|
||||||
StyledRect {
|
|
||||||
width: parent.width
|
|
||||||
height: appLauncherSection.implicitHeight + Theme.spacingL * 2
|
|
||||||
radius: Theme.cornerRadiusLarge
|
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: appLauncherSection
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Theme.spacingL
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "apps"
|
|
||||||
size: Theme.iconSize
|
|
||||||
color: Theme.primary
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "App Launcher"
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DankToggle {
|
|
||||||
width: parent.width
|
|
||||||
text: "Use OS Logo for App Launcher"
|
|
||||||
description: "Display operating system logo instead of apps icon"
|
|
||||||
checked: Prefs.useOSLogo
|
|
||||||
onToggled: (checked) => {
|
|
||||||
return Prefs.setUseOSLogo(checked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OS Logo Customization - only visible when OS logo is enabled
|
|
||||||
StyledRect {
|
|
||||||
width: parent.width
|
|
||||||
height: logoCustomization.implicitHeight + Theme.spacingM * 2
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
|
|
||||||
border.width: 1
|
|
||||||
visible: Prefs.useOSLogo
|
|
||||||
opacity: visible ? 1 : 0
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: logoCustomization
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Theme.spacingM
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "OS Logo Customization"
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Medium
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "Color Override"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Medium
|
|
||||||
}
|
|
||||||
|
|
||||||
DankTextField {
|
|
||||||
width: 160
|
|
||||||
height: 36
|
|
||||||
placeholderText: "#ffffff"
|
|
||||||
text: Prefs.osLogoColorOverride
|
|
||||||
maximumLength: 7
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
topPadding: Theme.spacingS
|
|
||||||
bottomPadding: Theme.spacingS
|
|
||||||
onEditingFinished: {
|
|
||||||
var color = text.trim();
|
|
||||||
if (color === "" || /^#[0-9A-Fa-f]{6}$/.test(color))
|
|
||||||
Prefs.setOSLogoColorOverride(color);
|
|
||||||
else
|
|
||||||
text = Prefs.osLogoColorOverride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "Brightness"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Medium
|
|
||||||
}
|
|
||||||
|
|
||||||
DankSlider {
|
|
||||||
width: parent.width
|
|
||||||
height: 24
|
|
||||||
minimum: 0
|
|
||||||
maximum: 100
|
|
||||||
value: Math.round(Prefs.osLogoBrightness * 100)
|
|
||||||
unit: ""
|
|
||||||
showValue: true
|
|
||||||
onSliderValueChanged: (newValue) => {
|
|
||||||
Prefs.setOSLogoBrightness(newValue / 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "Contrast"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Medium
|
|
||||||
}
|
|
||||||
|
|
||||||
DankSlider {
|
|
||||||
width: parent.width
|
|
||||||
height: 24
|
|
||||||
minimum: 0
|
|
||||||
maximum: 200
|
|
||||||
value: Math.round(Prefs.osLogoContrast * 100)
|
|
||||||
unit: ""
|
|
||||||
showValue: true
|
|
||||||
onSliderValueChanged: (newValue) => {
|
|
||||||
Prefs.setOSLogoContrast(newValue / 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,20 +116,20 @@ PanelWindow {
|
|||||||
// Use estimated fixed widths to break circular dependencies
|
// Use estimated fixed widths to break circular dependencies
|
||||||
readonly property int launcherButtonWidth: 40
|
readonly property int launcherButtonWidth: 40
|
||||||
readonly property int workspaceSwitcherWidth: 120 // Approximate
|
readonly property int workspaceSwitcherWidth: 120 // Approximate
|
||||||
readonly property int focusedAppMaxWidth: focusedApp.visible ? (topBarContent.spacingTight ? 288 : 456) : 0
|
readonly property int focusedAppMaxWidth: focusedApp.visible ? 456 : 0
|
||||||
readonly property int estimatedLeftSectionWidth: launcherButtonWidth + workspaceSwitcherWidth + focusedAppMaxWidth + (Theme.spacingXS * 2)
|
readonly property int estimatedLeftSectionWidth: launcherButtonWidth + workspaceSwitcherWidth + focusedAppMaxWidth + (Theme.spacingXS * 2)
|
||||||
readonly property int rightSectionWidth: rightSection.width
|
readonly property int rightSectionWidth: rightSection.width
|
||||||
readonly property int clockWidth: clock.width
|
readonly property int clockWidth: clock.width
|
||||||
readonly property int mediaMaxWidth: media.visible ? 280 : 0 // Normal max width
|
readonly property int mediaMaxWidth: media.visible ? 280 : 0 // Normal max width
|
||||||
readonly property int weatherWidth: weather.visible ? weather.width : 0
|
readonly property int weatherWidth: weather.visible ? weather.width : 0
|
||||||
readonly property bool validLayout: availableWidth > 100 && estimatedLeftSectionWidth > 0 && rightSectionWidth > 0
|
readonly property bool validLayout: availableWidth > 100 && estimatedLeftSectionWidth > 0 && rightSectionWidth > 0
|
||||||
readonly property int clockLeftEdge: validLayout ? (availableWidth - clockWidth) / 2 : 0
|
readonly property int clockLeftEdge: (availableWidth - clockWidth) / 2
|
||||||
readonly property int clockRightEdge: clockLeftEdge + clockWidth
|
readonly property int clockRightEdge: clockLeftEdge + clockWidth
|
||||||
readonly property int leftSectionRightEdge: estimatedLeftSectionWidth
|
readonly property int leftSectionRightEdge: estimatedLeftSectionWidth
|
||||||
readonly property int mediaLeftEdge: clockLeftEdge - mediaMaxWidth - Theme.spacingS
|
readonly property int mediaLeftEdge: clockLeftEdge - mediaMaxWidth - Theme.spacingS
|
||||||
readonly property int rightSectionLeftEdge: availableWidth - rightSectionWidth
|
readonly property int rightSectionLeftEdge: availableWidth - rightSectionWidth
|
||||||
readonly property int leftToClockGap: validLayout ? Math.max(0, clockLeftEdge - leftSectionRightEdge) : 1000
|
readonly property int leftToClockGap: Math.max(0, clockLeftEdge - leftSectionRightEdge)
|
||||||
readonly property int leftToMediaGap: (mediaMaxWidth > 0 && validLayout) ? Math.max(0, mediaLeftEdge - leftSectionRightEdge) : leftToClockGap
|
readonly property int leftToMediaGap: mediaMaxWidth > 0 ? Math.max(0, mediaLeftEdge - leftSectionRightEdge) : leftToClockGap
|
||||||
readonly property int mediaToClockGap: mediaMaxWidth > 0 ? Theme.spacingS : 0
|
readonly property int mediaToClockGap: mediaMaxWidth > 0 ? Theme.spacingS : 0
|
||||||
readonly property int clockToRightGap: validLayout ? Math.max(0, rightSectionLeftEdge - clockRightEdge) : 1000
|
readonly property int clockToRightGap: validLayout ? Math.max(0, rightSectionLeftEdge - clockRightEdge) : 1000
|
||||||
readonly property bool spacingTight: validLayout && (leftToMediaGap < 150 || clockToRightGap < 100)
|
readonly property bool spacingTight: validLayout && (leftToMediaGap < 150 || clockToRightGap < 100)
|
||||||
|
|||||||
@@ -119,7 +119,6 @@ Singleton {
|
|||||||
// Category icon mappings
|
// Category icon mappings
|
||||||
property var categoryIcons: ({
|
property var categoryIcons: ({
|
||||||
"All": "apps",
|
"All": "apps",
|
||||||
"Recents": "history",
|
|
||||||
"Media": "music_video",
|
"Media": "music_video",
|
||||||
"Development": "code",
|
"Development": "code",
|
||||||
"Games": "sports_esports",
|
"Games": "sports_esports",
|
||||||
|
|||||||
@@ -39,6 +39,10 @@ Rectangle {
|
|||||||
property real topPadding: Theme.spacingM
|
property real topPadding: Theme.spacingM
|
||||||
property real bottomPadding: Theme.spacingM
|
property real bottomPadding: Theme.spacingM
|
||||||
|
|
||||||
|
// Behavior control
|
||||||
|
property bool ignoreLeftRightKeys: false
|
||||||
|
property var keyForwardTargets: []
|
||||||
|
|
||||||
// Signals
|
// Signals
|
||||||
signal textEdited()
|
signal textEdited()
|
||||||
signal editingFinished()
|
signal editingFinished()
|
||||||
@@ -83,6 +87,10 @@ Rectangle {
|
|||||||
textInput.cut();
|
textInput.cut();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function insertText(str) {
|
||||||
|
textInput.insert(textInput.cursorPosition, str);
|
||||||
|
}
|
||||||
|
|
||||||
function clearFocus() {
|
function clearFocus() {
|
||||||
textInput.focus = false;
|
textInput.focus = false;
|
||||||
}
|
}
|
||||||
@@ -119,13 +127,27 @@ Rectangle {
|
|||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
selectByMouse: true
|
selectByMouse: !root.ignoreLeftRightKeys
|
||||||
clip: true
|
clip: true
|
||||||
onTextChanged: root.textEdited()
|
onTextChanged: root.textEdited()
|
||||||
onEditingFinished: root.editingFinished()
|
onEditingFinished: root.editingFinished()
|
||||||
onAccepted: root.accepted()
|
onAccepted: root.accepted()
|
||||||
onActiveFocusChanged: root.focusStateChanged(activeFocus)
|
onActiveFocusChanged: root.focusStateChanged(activeFocus)
|
||||||
|
|
||||||
|
Keys.forwardTo: root.ignoreLeftRightKeys ? root.keyForwardTargets : []
|
||||||
|
|
||||||
|
Keys.onLeftPressed: function(event) {
|
||||||
|
if (root.ignoreLeftRightKeys) {
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onRightPressed: function(event) {
|
||||||
|
if (root.ignoreLeftRightKeys) {
|
||||||
|
event.accepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
|
|||||||
Reference in New Issue
Block a user