Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
e5936aa730
|
|||
| c33d6ae2dd | |||
| ca19a60e5c | |||
|
233ea3efb9
|
|||
|
d19eead1f5
|
|||
|
d0b2a5fc1d
|
|||
|
32acfa6b9f
|
|||
|
17fcf1a02c
|
|||
|
1c11549811
|
+1
-12
@@ -22,7 +22,6 @@ Singleton {
|
|||||||
property alias notifs: adapter.notifs
|
property alias notifs: adapter.notifs
|
||||||
property alias osd: adapter.osd
|
property alias osd: adapter.osd
|
||||||
property alias overview: adapter.overview
|
property alias overview: adapter.overview
|
||||||
property alias plugins: adapter.plugins
|
|
||||||
property bool recentlySaved: false
|
property bool recentlySaved: false
|
||||||
property alias screenshot: adapter.screenshot
|
property alias screenshot: adapter.screenshot
|
||||||
property alias services: adapter.services
|
property alias services: adapter.services
|
||||||
@@ -141,8 +140,7 @@ Singleton {
|
|||||||
launcher: serializeLauncher(),
|
launcher: serializeLauncher(),
|
||||||
colors: serializeColors(),
|
colors: serializeColors(),
|
||||||
dock: serializeDock(),
|
dock: serializeDock(),
|
||||||
screenshot: serializeScreenshot(),
|
screenshot: serializeScreenshot()
|
||||||
plugins: serializePlugins()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,13 +289,6 @@ Singleton {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function serializePlugins(): var {
|
|
||||||
return {
|
|
||||||
enabled: plugins.enabled,
|
|
||||||
entries: plugins.entries
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function serializeScreenshot(): var {
|
function serializeScreenshot(): var {
|
||||||
return {
|
return {
|
||||||
enable_pp: screenshot.enable_pp,
|
enable_pp: screenshot.enable_pp,
|
||||||
@@ -467,8 +458,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
property Overview overview: Overview {
|
property Overview overview: Overview {
|
||||||
}
|
}
|
||||||
property PluginConfig plugins: PluginConfig {
|
|
||||||
}
|
|
||||||
property Screenshot screenshot: Screenshot {
|
property Screenshot screenshot: Screenshot {
|
||||||
}
|
}
|
||||||
property Services services: Services {
|
property Services services: Services {
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import Quickshell.Io
|
|
||||||
|
|
||||||
JsonObject {
|
|
||||||
property bool enabled: false
|
|
||||||
property list<var> entries: [
|
|
||||||
{
|
|
||||||
id: "Plugin",
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
+1
-1
@@ -35,7 +35,7 @@ Variants {
|
|||||||
property var root: Quickshell.shellDir
|
property var root: Quickshell.shellDir
|
||||||
|
|
||||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||||
WlrLayershell.keyboardFocus: visibilities.dock || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.resources ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
// WlrLayershell.keyboardFocus: visibilities.dock || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.resources ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
contentItem.focus: true
|
contentItem.focus: true
|
||||||
mask: visibilities.isDrawing ? null : region
|
mask: visibilities.isDrawing ? null : region
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
pragma Singleton
|
|
||||||
|
|
||||||
import Quickshell
|
|
||||||
import ZShell.Models
|
|
||||||
|
|
||||||
Singleton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property alias plugins: plugins.entries
|
|
||||||
|
|
||||||
FileSystemModel {
|
|
||||||
id: plugins
|
|
||||||
|
|
||||||
nameFilters: ["*.qml"]
|
|
||||||
path: Quickshell.env("HOME") + "/.config/zshell"
|
|
||||||
recursive: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import Quickshell
|
|
||||||
import QtQuick
|
|
||||||
import ZShell.Models
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: FetchPlugins.plugins
|
|
||||||
|
|
||||||
LazyLoader {
|
|
||||||
required property FileSystemEntry modelData
|
|
||||||
|
|
||||||
activeAsync: Config.plugins.entries.some(p => {
|
|
||||||
return p.id === modelData.baseName && p.enabled;
|
|
||||||
})
|
|
||||||
source: modelData.path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ import Quickshell
|
|||||||
Singleton {
|
Singleton {
|
||||||
property var extraOpts: ({})
|
property var extraOpts: ({})
|
||||||
readonly property list<var> fuzzyPrepped: useFuzzy ? list.map(e => {
|
readonly property list<var> fuzzyPrepped: useFuzzy ? list.map(e => {
|
||||||
|
console.log(useFuzzy);
|
||||||
const obj = {
|
const obj = {
|
||||||
_item: e
|
_item: e
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import Quickshell
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Config
|
import qs.Config
|
||||||
|
import qs.Modules.Launcher.Services
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -19,26 +20,17 @@ Item {
|
|||||||
max -= panels.popouts.nonAnimHeight;
|
max -= panels.popouts.nonAnimHeight;
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
property real offsetScale: shouldBeActive ? 0 : 1
|
||||||
required property var panels
|
required property var panels
|
||||||
required property ShellScreen screen
|
required property ShellScreen screen
|
||||||
required property PersistentProperties visibilities
|
|
||||||
readonly property bool shouldBeActive: visibilities.launcher
|
readonly property bool shouldBeActive: visibilities.launcher
|
||||||
property real offsetScale: shouldBeActive ? 0 : 1
|
required property PersistentProperties visibilities
|
||||||
|
|
||||||
onShouldBeActiveChanged: {
|
|
||||||
if (shouldBeActive) {
|
|
||||||
implicitHeight = Qt.binding(() => content.implicitHeight);
|
|
||||||
timer.stop();
|
|
||||||
} else {
|
|
||||||
implicitHeight = implicitHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visible: offsetScale < 1
|
|
||||||
anchors.bottomMargin: (-implicitHeight - 5) * offsetScale
|
anchors.bottomMargin: (-implicitHeight - 5) * offsetScale
|
||||||
implicitHeight: content.implicitHeight
|
implicitHeight: content.implicitHeight
|
||||||
implicitWidth: content.implicitWidth || 400
|
implicitWidth: content.implicitWidth || 400
|
||||||
opacity: 1 - offsetScale
|
opacity: 1 - offsetScale
|
||||||
|
visible: offsetScale < 1
|
||||||
|
|
||||||
Behavior on offsetScale {
|
Behavior on offsetScale {
|
||||||
Anim {
|
Anim {
|
||||||
@@ -47,61 +39,26 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMaxHeightChanged: timer.start()
|
Component.onCompleted: Qt.callLater(() => Apps)
|
||||||
|
onShouldBeActiveChanged: {
|
||||||
Connections {
|
if (shouldBeActive)
|
||||||
function onEnabledChanged(): void {
|
implicitHeight = Qt.binding(() => content.implicitHeight);
|
||||||
timer.start();
|
else
|
||||||
}
|
implicitHeight = implicitHeight;
|
||||||
|
|
||||||
function onMaxShownChanged(): void {
|
|
||||||
timer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: Config.launcher
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
function onValuesChanged(): void {
|
|
||||||
if (DesktopEntries.applications.values.length < Config.launcher.maxAppsShown)
|
|
||||||
timer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
target: DesktopEntries.applications
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: timer
|
|
||||||
|
|
||||||
interval: Appearance.anim.durations.small
|
|
||||||
|
|
||||||
onRunningChanged: {
|
|
||||||
if (running && !root.shouldBeActive) {
|
|
||||||
content.visible = false;
|
|
||||||
content.active = true;
|
|
||||||
} else {
|
|
||||||
root.contentHeight = Math.min(root.maxHeight, content.implicitHeight);
|
|
||||||
content.active = Qt.binding(() => root.shouldBeActive || root.visible);
|
|
||||||
content.visible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: content
|
id: content
|
||||||
|
|
||||||
active: false
|
active: root.shouldBeActive || root.visible
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: Content {
|
sourceComponent: Content {
|
||||||
maxHeight: root.maxHeight
|
maxHeight: root.maxHeight
|
||||||
panels: root.panels
|
panels: root.panels
|
||||||
visibilities: root.visibilities
|
visibilities: root.visibilities
|
||||||
|
|
||||||
Component.onCompleted: root.contentHeight = implicitHeight
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: timer.start()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,12 +116,6 @@ Item {
|
|||||||
key: "updates"
|
key: "updates"
|
||||||
name: "Updates"
|
name: "Updates"
|
||||||
}
|
}
|
||||||
|
|
||||||
ListElement {
|
|
||||||
icon: "extension"
|
|
||||||
key: "plugins"
|
|
||||||
name: "Extensions"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomClippingRect {
|
CustomClippingRect {
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import qs.Modules.Settings.Controls
|
|
||||||
import qs.Config
|
|
||||||
|
|
||||||
SettingsPage {
|
|
||||||
SettingsSection {
|
|
||||||
sectionId: "Plugins"
|
|
||||||
|
|
||||||
SettingsHeader {
|
|
||||||
name: "Plugins"
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingBarEntryList {
|
|
||||||
name: "Enable or disable plugins"
|
|
||||||
object: Config.plugins
|
|
||||||
setting: "entries"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -79,8 +79,6 @@ Item {
|
|||||||
stack.push(screenshot);
|
stack.push(screenshot);
|
||||||
else if (currentCategory === "updates")
|
else if (currentCategory === "updates")
|
||||||
stack.push(updates);
|
stack.push(updates);
|
||||||
else if (currentCategory === "plugins")
|
|
||||||
stack.push(plugins);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target: root
|
target: root
|
||||||
@@ -247,11 +245,4 @@ Item {
|
|||||||
Cat.SystemUpdates {
|
Cat.SystemUpdates {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
|
||||||
id: plugins
|
|
||||||
|
|
||||||
Cat.Plugins {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import qs.Modules.DesktopIcons
|
|||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
active: Config.background.enabled
|
active: Config.background.enabled
|
||||||
asynchronous: true
|
asynchronous: false
|
||||||
|
|
||||||
sourceComponent: Variants {
|
sourceComponent: Variants {
|
||||||
model: Quickshell.screens
|
model: Quickshell.screens
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ FileSystemEntry::FileSystemEntry(const QString& path, const QString& relativePat
|
|||||||
, m_path(path)
|
, m_path(path)
|
||||||
, m_relativePath(relativePath)
|
, m_relativePath(relativePath)
|
||||||
, m_isImageInitialised(false)
|
, m_isImageInitialised(false)
|
||||||
, m_mimeTypeInitialised(false) {
|
, m_mimeTypeInitialised(false) {}
|
||||||
}
|
|
||||||
|
|
||||||
QString FileSystemEntry::path() const {
|
QString FileSystemEntry::path() const {
|
||||||
return m_path;
|
return m_path;
|
||||||
@@ -58,8 +57,8 @@ bool FileSystemEntry::isImage() const {
|
|||||||
|
|
||||||
QString FileSystemEntry::mimeType() const {
|
QString FileSystemEntry::mimeType() const {
|
||||||
if (!m_mimeTypeInitialised) {
|
if (!m_mimeTypeInitialised) {
|
||||||
static const QMimeDatabase s_db;
|
const QMimeDatabase db;
|
||||||
m_mimeType = s_db.mimeTypeForFile(m_path).name();
|
m_mimeType = db.mimeTypeForFile(m_path).name();
|
||||||
m_mimeTypeInitialised = true;
|
m_mimeTypeInitialised = true;
|
||||||
}
|
}
|
||||||
return m_mimeType;
|
return m_mimeType;
|
||||||
@@ -220,7 +219,7 @@ void FileSystemModel::watchDirIfRecursive(const QString& path) {
|
|||||||
if (m_recursive && m_watchChanges) {
|
if (m_recursive && m_watchChanges) {
|
||||||
const auto currentDir = m_dir;
|
const auto currentDir = m_dir;
|
||||||
const bool showHidden = m_showHidden;
|
const bool showHidden = m_showHidden;
|
||||||
auto future = QtConcurrent::run([showHidden, path]() {
|
const auto future = QtConcurrent::run([showHidden, path]() {
|
||||||
QDir::Filters filters = QDir::Dirs | QDir::NoDotAndDotDot;
|
QDir::Filters filters = QDir::Dirs | QDir::NoDotAndDotDot;
|
||||||
if (showHidden) {
|
if (showHidden) {
|
||||||
filters |= QDir::Hidden;
|
filters |= QDir::Hidden;
|
||||||
@@ -233,12 +232,16 @@ void FileSystemModel::watchDirIfRecursive(const QString& path) {
|
|||||||
}
|
}
|
||||||
return dirs;
|
return dirs;
|
||||||
});
|
});
|
||||||
future.then(this, [currentDir, showHidden, this](const QStringList& paths) {
|
const auto watcher = new QFutureWatcher<QStringList>(this);
|
||||||
|
connect(watcher, &QFutureWatcher<QStringList>::finished, this, [currentDir, showHidden, watcher, this]() {
|
||||||
|
const auto paths = watcher->result();
|
||||||
if (currentDir == m_dir && showHidden == m_showHidden && !paths.isEmpty()) {
|
if (currentDir == m_dir && showHidden == m_showHidden && !paths.isEmpty()) {
|
||||||
// Ignore if dir or showHidden has changed
|
// Ignore if dir or showHidden has changed
|
||||||
m_watcher.addPaths(paths);
|
m_watcher.addPaths(paths);
|
||||||
}
|
}
|
||||||
|
watcher->deleteLater();
|
||||||
});
|
});
|
||||||
|
watcher->setFuture(future);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,7 +295,7 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
|
|||||||
oldPaths << entry->path();
|
oldPaths << entry->path();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto future = QtConcurrent::run([=](QPromise<QPair<QSet<QString>, QSet<QString> > >& promise) {
|
const auto future = QtConcurrent::run([=](QPromise<QPair<QSet<QString>, QSet<QString>>>& promise) {
|
||||||
const auto flags = recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags;
|
const auto flags = recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags;
|
||||||
|
|
||||||
std::optional<QDirIterator> iter;
|
std::optional<QDirIterator> iter;
|
||||||
@@ -350,7 +353,7 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
|
|||||||
newPaths.insert(path);
|
newPaths.insert(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (promise.isCanceled()) {
|
if (promise.isCanceled() || newPaths == oldPaths) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,17 +365,23 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
|
|||||||
}
|
}
|
||||||
m_futures.insert(dir, future);
|
m_futures.insert(dir, future);
|
||||||
|
|
||||||
future
|
const auto watcher = new QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>(this);
|
||||||
.then(this,
|
|
||||||
[dir, this](QPair<QSet<QString>, QSet<QString> > result) {
|
connect(watcher, &QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>::finished, this, [dir, watcher, this]() {
|
||||||
m_futures.remove(dir);
|
m_futures.remove(dir);
|
||||||
if (!result.first.isEmpty() || !result.second.isEmpty()) {
|
|
||||||
applyChanges(result.first, result.second);
|
if (!watcher->future().isResultReadyAt(0)) {
|
||||||
|
watcher->deleteLater();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.onCanceled(this, [dir, this]() {
|
const auto result = watcher->result();
|
||||||
m_futures.remove(dir);
|
applyChanges(result.first, result.second);
|
||||||
|
|
||||||
|
watcher->deleteLater();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watcher->setFuture(future);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths) {
|
void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths) {
|
||||||
|
|||||||
@@ -13,136 +13,136 @@
|
|||||||
namespace ZShell::models {
|
namespace ZShell::models {
|
||||||
|
|
||||||
class FileSystemEntry : public QObject {
|
class FileSystemEntry : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
QML_UNCREATABLE("FileSystemEntry instances can only be retrieved from a FileSystemModel")
|
QML_UNCREATABLE("FileSystemEntry instances can only be retrieved from a FileSystemModel")
|
||||||
|
|
||||||
Q_PROPERTY(QString path READ path CONSTANT)
|
Q_PROPERTY(QString path READ path CONSTANT)
|
||||||
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
|
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
|
||||||
Q_PROPERTY(QString name READ name CONSTANT)
|
Q_PROPERTY(QString name READ name CONSTANT)
|
||||||
Q_PROPERTY(QString baseName READ baseName CONSTANT)
|
Q_PROPERTY(QString baseName READ baseName CONSTANT)
|
||||||
Q_PROPERTY(QString parentDir READ parentDir CONSTANT)
|
Q_PROPERTY(QString parentDir READ parentDir CONSTANT)
|
||||||
Q_PROPERTY(QString suffix READ suffix CONSTANT)
|
Q_PROPERTY(QString suffix READ suffix CONSTANT)
|
||||||
Q_PROPERTY(qint64 size READ size CONSTANT)
|
Q_PROPERTY(qint64 size READ size CONSTANT)
|
||||||
Q_PROPERTY(bool isDir READ isDir CONSTANT)
|
Q_PROPERTY(bool isDir READ isDir CONSTANT)
|
||||||
Q_PROPERTY(bool isImage READ isImage CONSTANT)
|
Q_PROPERTY(bool isImage READ isImage CONSTANT)
|
||||||
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
|
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent = nullptr);
|
explicit FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent = nullptr);
|
||||||
|
|
||||||
[[nodiscard]] QString path() const;
|
[[nodiscard]] QString path() const;
|
||||||
[[nodiscard]] QString relativePath() const;
|
[[nodiscard]] QString relativePath() const;
|
||||||
[[nodiscard]] QString name() const;
|
[[nodiscard]] QString name() const;
|
||||||
[[nodiscard]] QString baseName() const;
|
[[nodiscard]] QString baseName() const;
|
||||||
[[nodiscard]] QString parentDir() const;
|
[[nodiscard]] QString parentDir() const;
|
||||||
[[nodiscard]] QString suffix() const;
|
[[nodiscard]] QString suffix() const;
|
||||||
[[nodiscard]] qint64 size() const;
|
[[nodiscard]] qint64 size() const;
|
||||||
[[nodiscard]] bool isDir() const;
|
[[nodiscard]] bool isDir() const;
|
||||||
[[nodiscard]] bool isImage() const;
|
[[nodiscard]] bool isImage() const;
|
||||||
[[nodiscard]] QString mimeType() const;
|
[[nodiscard]] QString mimeType() const;
|
||||||
|
|
||||||
void updateRelativePath(const QDir& dir);
|
void updateRelativePath(const QDir& dir);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void relativePathChanged();
|
void relativePathChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const QFileInfo m_fileInfo;
|
const QFileInfo m_fileInfo;
|
||||||
|
|
||||||
const QString m_path;
|
const QString m_path;
|
||||||
QString m_relativePath;
|
QString m_relativePath;
|
||||||
|
|
||||||
mutable bool m_isImage;
|
mutable bool m_isImage;
|
||||||
mutable bool m_isImageInitialised;
|
mutable bool m_isImageInitialised;
|
||||||
|
|
||||||
mutable QString m_mimeType;
|
mutable QString m_mimeType;
|
||||||
mutable bool m_mimeTypeInitialised;
|
mutable bool m_mimeTypeInitialised;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileSystemModel : public QAbstractListModel {
|
class FileSystemModel : public QAbstractListModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||||
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
|
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
|
||||||
Q_PROPERTY(bool watchChanges READ watchChanges WRITE setWatchChanges NOTIFY watchChangesChanged)
|
Q_PROPERTY(bool watchChanges READ watchChanges WRITE setWatchChanges NOTIFY watchChangesChanged)
|
||||||
Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden NOTIFY showHiddenChanged)
|
Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden NOTIFY showHiddenChanged)
|
||||||
Q_PROPERTY(bool sortReverse READ sortReverse WRITE setSortReverse NOTIFY sortReverseChanged)
|
Q_PROPERTY(bool sortReverse READ sortReverse WRITE setSortReverse NOTIFY sortReverseChanged)
|
||||||
Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged)
|
Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged)
|
||||||
Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
|
Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
|
||||||
|
|
||||||
Q_PROPERTY(QQmlListProperty<ZShell::models::FileSystemEntry> entries READ entries NOTIFY entriesChanged)
|
Q_PROPERTY(QQmlListProperty<ZShell::models::FileSystemEntry> entries READ entries NOTIFY entriesChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Filter {
|
enum Filter {
|
||||||
NoFilter,
|
NoFilter,
|
||||||
Images,
|
Images,
|
||||||
Files,
|
Files,
|
||||||
Dirs
|
Dirs
|
||||||
};
|
};
|
||||||
Q_ENUM(Filter)
|
Q_ENUM(Filter)
|
||||||
|
|
||||||
explicit FileSystemModel(QObject* parent = nullptr);
|
explicit FileSystemModel(QObject* parent = nullptr);
|
||||||
|
|
||||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
[[nodiscard]] QString path() const;
|
[[nodiscard]] QString path() const;
|
||||||
void setPath(const QString& path);
|
void setPath(const QString& path);
|
||||||
|
|
||||||
[[nodiscard]] bool recursive() const;
|
[[nodiscard]] bool recursive() const;
|
||||||
void setRecursive(bool recursive);
|
void setRecursive(bool recursive);
|
||||||
|
|
||||||
[[nodiscard]] bool watchChanges() const;
|
[[nodiscard]] bool watchChanges() const;
|
||||||
void setWatchChanges(bool watchChanges);
|
void setWatchChanges(bool watchChanges);
|
||||||
|
|
||||||
[[nodiscard]] bool showHidden() const;
|
[[nodiscard]] bool showHidden() const;
|
||||||
void setShowHidden(bool showHidden);
|
void setShowHidden(bool showHidden);
|
||||||
|
|
||||||
[[nodiscard]] bool sortReverse() const;
|
[[nodiscard]] bool sortReverse() const;
|
||||||
void setSortReverse(bool sortReverse);
|
void setSortReverse(bool sortReverse);
|
||||||
|
|
||||||
[[nodiscard]] Filter filter() const;
|
[[nodiscard]] Filter filter() const;
|
||||||
void setFilter(Filter filter);
|
void setFilter(Filter filter);
|
||||||
|
|
||||||
[[nodiscard]] QStringList nameFilters() const;
|
[[nodiscard]] QStringList nameFilters() const;
|
||||||
void setNameFilters(const QStringList& nameFilters);
|
void setNameFilters(const QStringList& nameFilters);
|
||||||
|
|
||||||
[[nodiscard]] QQmlListProperty<FileSystemEntry> entries();
|
[[nodiscard]] QQmlListProperty<FileSystemEntry> entries();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void pathChanged();
|
void pathChanged();
|
||||||
void recursiveChanged();
|
void recursiveChanged();
|
||||||
void watchChangesChanged();
|
void watchChangesChanged();
|
||||||
void showHiddenChanged();
|
void showHiddenChanged();
|
||||||
void sortReverseChanged();
|
void sortReverseChanged();
|
||||||
void filterChanged();
|
void filterChanged();
|
||||||
void nameFiltersChanged();
|
void nameFiltersChanged();
|
||||||
void entriesChanged();
|
void entriesChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDir m_dir;
|
QDir m_dir;
|
||||||
QFileSystemWatcher m_watcher;
|
QFileSystemWatcher m_watcher;
|
||||||
QList<FileSystemEntry*> m_entries;
|
QList<FileSystemEntry*> m_entries;
|
||||||
QHash<QString, QFuture<QPair<QSet<QString>, QSet<QString> > > > m_futures;
|
QHash<QString, QFuture<QPair<QSet<QString>, QSet<QString>>>> m_futures;
|
||||||
|
|
||||||
QString m_path;
|
QString m_path;
|
||||||
bool m_recursive;
|
bool m_recursive;
|
||||||
bool m_watchChanges;
|
bool m_watchChanges;
|
||||||
bool m_showHidden;
|
bool m_showHidden;
|
||||||
bool m_sortReverse = false;
|
bool m_sortReverse;
|
||||||
Filter m_filter;
|
Filter m_filter;
|
||||||
QStringList m_nameFilters;
|
QStringList m_nameFilters;
|
||||||
|
|
||||||
void watchDirIfRecursive(const QString& path);
|
void watchDirIfRecursive(const QString& path);
|
||||||
void update();
|
void update();
|
||||||
void updateWatcher();
|
void updateWatcher();
|
||||||
void updateEntries();
|
void updateEntries();
|
||||||
void updateEntriesForDir(const QString& dir);
|
void updateEntriesForDir(const QString& dir);
|
||||||
void applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
|
void applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
|
||||||
[[nodiscard]] bool compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const;
|
[[nodiscard]] bool compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell::models
|
} // namespace ZShell::models
|
||||||
|
|||||||
@@ -1,16 +1,53 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import click
|
||||||
import typer
|
import typer
|
||||||
|
from typer._completion_shared import install, _get_shell_name
|
||||||
from zshell.subcommands import shell, scheme, screenshot, wallpaper, record
|
from zshell.subcommands import shell, scheme, screenshot, wallpaper, record
|
||||||
|
|
||||||
app = typer.Typer()
|
app = typer.Typer(name="zshell-cli", add_completion=False)
|
||||||
|
|
||||||
app.add_typer(shell.app, name="shell")
|
app.add_typer(shell.app, name="shell")
|
||||||
app.add_typer(scheme.app, name="scheme")
|
app.add_typer(scheme.app, name="scheme")
|
||||||
app.add_typer(screenshot.app, name="screenshot")
|
app.add_typer(screenshot.app, name="screenshot")
|
||||||
app.add_typer(wallpaper.app, name="wallpaper")
|
app.add_typer(wallpaper.app, name="wallpaper")
|
||||||
app.add_typer(record.app, name="record")
|
app.add_typer(record.app, name="record")
|
||||||
# app.add_typer(preset.app, name="preset")
|
|
||||||
|
|
||||||
|
def _completion_installed() -> bool:
|
||||||
|
shell = _get_shell_name()
|
||||||
|
match shell:
|
||||||
|
case "zsh":
|
||||||
|
return (Path.home() / ".zfunc" / "_zshell-cli").exists()
|
||||||
|
case "bash":
|
||||||
|
return (Path.home() / ".bash_completions" / "zshell-cli.sh").exists()
|
||||||
|
case "fish":
|
||||||
|
return (Path.home() / ".config" / "fish" / "completions" / "zshell-cli.fish").exists()
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _install_completion() -> None:
|
||||||
|
if _completion_installed():
|
||||||
|
click.echo("zshell-cli: Shell completion already installed.")
|
||||||
|
raise typer.Exit()
|
||||||
|
shell = _get_shell_name()
|
||||||
|
if shell is None:
|
||||||
|
click.echo("zshell-cli: Unable to detect shell type.", err=True)
|
||||||
|
raise typer.Exit(code=1)
|
||||||
|
try:
|
||||||
|
_, path = install(prog_name="zshell-cli")
|
||||||
|
click.secho(f"zshell-cli: Shell completion installed ({shell}: {path})", fg="green")
|
||||||
|
click.echo("zshell-cli: Restart your shell or source the file to enable tab-completion.")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
if "--install-autocomplete" in sys.argv:
|
||||||
|
_install_completion()
|
||||||
|
return
|
||||||
|
if sys.stdout.isatty() and not _completion_installed():
|
||||||
|
click.echo("zshell-cli: Tip: run with --install-autocomplete for tab completion.", err=True)
|
||||||
app()
|
app()
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ TEMP_RECORDING = STATE_DIR / "recording.mp4"
|
|||||||
REPLAY_RECORDING = STATE_DIR / "replay.mp4"
|
REPLAY_RECORDING = STATE_DIR / "replay.mp4"
|
||||||
NOTIF_ID_FILE = STATE_DIR / "notifid.txt"
|
NOTIF_ID_FILE = STATE_DIR / "notifid.txt"
|
||||||
|
|
||||||
RECORDINGS_DIR = os.getenv("ZSHELL_RECORDINGS_DIR",
|
RECORDINGS_DIR = os.getenv("ZSHELL_RECORDINGS_DIR", str(Path(HOME) / "Videos/Recordings"))
|
||||||
str(Path(HOME) / "Videos/Recordings"))
|
|
||||||
|
|
||||||
|
|
||||||
def _read_extra_args() -> list[str]:
|
def _read_extra_args() -> list[str]:
|
||||||
@@ -36,7 +35,7 @@ def _is_recording() -> bool:
|
|||||||
return subprocess.run(["pidof", RECORDER], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0
|
return subprocess.run(["pidof", RECORDER], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def _notify(summary: str, body: str = "", actions: list = None, timeout: int = 5000) -> Optional[int]:
|
def _notify(summary: str, body: str = "", actions: list | None = None, timeout: int = 5000) -> Optional[int]:
|
||||||
args = ["notify-send", summary, body, "-t", str(timeout), "-p"]
|
args = ["notify-send", summary, body, "-t", str(timeout), "-p"]
|
||||||
if actions:
|
if actions:
|
||||||
for action in actions:
|
for action in actions:
|
||||||
@@ -49,14 +48,12 @@ def _notify(summary: str, body: str = "", actions: list = None, timeout: int = 5
|
|||||||
|
|
||||||
|
|
||||||
def _close_notification(notif_id: int):
|
def _close_notification(notif_id: int):
|
||||||
subprocess.run(["notify-send", "--close", str(notif_id)],
|
subprocess.run(["notify-send", "--close", str(notif_id)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_monitors() -> list[dict]:
|
def _get_monitors() -> list[dict]:
|
||||||
try:
|
try:
|
||||||
res = subprocess.run(["hyprctl", "monitors", "-j"],
|
res = subprocess.run(["hyprctl", "monitors", "-j"], capture_output=True, text=True)
|
||||||
capture_output=True, text=True)
|
|
||||||
return json.loads(res.stdout)
|
return json.loads(res.stdout)
|
||||||
except Exception:
|
except Exception:
|
||||||
return []
|
return []
|
||||||
@@ -92,6 +89,7 @@ def _slurp_region() -> Optional[str]:
|
|||||||
|
|
||||||
def _parse_geometry(geometry: str) -> Optional[tuple[int, int, int, int]]:
|
def _parse_geometry(geometry: str) -> Optional[tuple[int, int, int, int]]:
|
||||||
import re
|
import re
|
||||||
|
|
||||||
match = re.match(r"(\d+)x(\d+)\+(\d+)\+(\d+)", geometry)
|
match = re.match(r"(\d+)x(\d+)\+(\d+)\+(\d+)", geometry)
|
||||||
if match:
|
if match:
|
||||||
return int(match.group(3)), int(match.group(4)), int(match.group(1)), int(match.group(2))
|
return int(match.group(3)), int(match.group(4)), int(match.group(1)), int(match.group(2))
|
||||||
@@ -139,8 +137,7 @@ def start_recording(region: Optional[str], sound: bool):
|
|||||||
cmd.extend(extra_args)
|
cmd.extend(extra_args)
|
||||||
cmd.extend(["-o", str(TEMP_RECORDING)])
|
cmd.extend(["-o", str(TEMP_RECORDING)])
|
||||||
|
|
||||||
subprocess.Popen(cmd, start_new_session=True,
|
subprocess.Popen(cmd, start_new_session=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
||||||
|
|
||||||
notif_id = _notify("Recording started", f"Saving to {TEMP_RECORDING}")
|
notif_id = _notify("Recording started", f"Saving to {TEMP_RECORDING}")
|
||||||
if notif_id is not None:
|
if notif_id is not None:
|
||||||
@@ -148,14 +145,12 @@ def start_recording(region: Optional[str], sound: bool):
|
|||||||
|
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
if not _is_recording():
|
if not _is_recording():
|
||||||
_notify("Recording failed",
|
_notify("Recording failed", "Check gpu-screen-recorder output.", timeout=5000)
|
||||||
"Check gpu-screen-recorder output.", timeout=5000)
|
|
||||||
raise typer.Exit(code=1)
|
raise typer.Exit(code=1)
|
||||||
|
|
||||||
|
|
||||||
def stop_recording(clipboard: bool):
|
def stop_recording(clipboard: bool):
|
||||||
subprocess.run(["pkill", "-f", RECORDER],
|
subprocess.run(["pkill", "-f", RECORDER], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
||||||
|
|
||||||
for _ in range(50):
|
for _ in range(50):
|
||||||
if not _is_recording():
|
if not _is_recording():
|
||||||
@@ -178,30 +173,31 @@ def stop_recording(clipboard: bool):
|
|||||||
NOTIF_ID_FILE.unlink()
|
NOTIF_ID_FILE.unlink()
|
||||||
|
|
||||||
if clipboard:
|
if clipboard:
|
||||||
subprocess.run(["wl-copy", "--type", "text/uri-list", f"file://{final_path}"],
|
subprocess.run(
|
||||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
["wl-copy", "--type", "text/uri-list", f"file://{final_path}"],
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
|
||||||
_notify("Recording stopped", f"Saved to {final_path}", timeout=5000)
|
_notify("Recording stopped", f"Saved to {final_path}", timeout=5000)
|
||||||
|
|
||||||
|
|
||||||
def toggle_pause():
|
def toggle_pause():
|
||||||
subprocess.run(["pkill", "-USR2", "-f", RECORDER],
|
subprocess.run(["pkill", "-USR2", "-f", RECORDER], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
||||||
typer.echo("Toggled pause.")
|
typer.echo("Toggled pause.")
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def record(
|
def record(
|
||||||
region: Optional[str] = typer.Option(
|
region: Optional[str] = typer.Option(
|
||||||
None, "--region", "-r",
|
None,
|
||||||
|
"--region",
|
||||||
|
"-r",
|
||||||
help="Record a region. Use 'slurp' (or omit value) to select interactively, or give 'WxH+X+Y'.",
|
help="Record a region. Use 'slurp' (or omit value) to select interactively, or give 'WxH+X+Y'.",
|
||||||
),
|
),
|
||||||
sound: bool = typer.Option(
|
sound: bool = typer.Option(False, "--sound", "-s", help="Record audio from default output."),
|
||||||
False, "--sound", "-s", help="Record audio from default output."),
|
pause: bool = typer.Option(False, "--pause", "-p", help="Toggle pause/resume."),
|
||||||
pause: bool = typer.Option(
|
clipboard: bool = typer.Option(False, "--clipboard", "-c", help="Copy the final recording path to clipboard."),
|
||||||
False, "--pause", "-p", help="Toggle pause/resume."),
|
|
||||||
clipboard: bool = typer.Option(
|
|
||||||
False, "--clipboard", "-c", help="Copy the final recording path to clipboard."),
|
|
||||||
):
|
):
|
||||||
"""Start or stop a screen recording with gpu-screen-recorder."""
|
"""Start or stop a screen recording with gpu-screen-recorder."""
|
||||||
if pause:
|
if pause:
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import typer
|
|||||||
import json
|
import json
|
||||||
import shutil
|
import shutil
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
@@ -15,11 +16,61 @@ from materialyoucolor.score.score import Score
|
|||||||
from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors
|
from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors
|
||||||
from materialyoucolor.hct.hct import Hct
|
from materialyoucolor.hct.hct import Hct
|
||||||
from materialyoucolor.utils.color_utils import argb_from_rgb
|
from materialyoucolor.utils.color_utils import argb_from_rgb
|
||||||
from materialyoucolor.utils.math_utils import difference_degrees, rotation_direction, sanitize_degrees_double
|
from materialyoucolor.utils.math_utils import (
|
||||||
|
difference_degrees,
|
||||||
|
rotation_direction,
|
||||||
|
sanitize_degrees_double,
|
||||||
|
)
|
||||||
|
|
||||||
app = typer.Typer()
|
app = typer.Typer()
|
||||||
|
|
||||||
|
|
||||||
|
def _complete_scheme_name(incomplete):
|
||||||
|
schemes = [
|
||||||
|
"fruit-salad",
|
||||||
|
"expressive",
|
||||||
|
"monochrome",
|
||||||
|
"rainbow",
|
||||||
|
"tonal-spot",
|
||||||
|
"neutral",
|
||||||
|
"fidelity",
|
||||||
|
"content",
|
||||||
|
"vibrant",
|
||||||
|
]
|
||||||
|
return [s for s in schemes if incomplete in s]
|
||||||
|
|
||||||
|
|
||||||
|
def _complete_preset(incomplete):
|
||||||
|
results = []
|
||||||
|
for sid, meta in list_schemes().items():
|
||||||
|
for v in meta.variants:
|
||||||
|
preset = f"{sid}:{v.id}"
|
||||||
|
if incomplete in preset:
|
||||||
|
results.append((preset, f"{meta.name} - {v.name}"))
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def _complete_mode(incomplete):
|
||||||
|
return [m for m in ("dark", "light") if incomplete in m]
|
||||||
|
|
||||||
|
|
||||||
|
def _complete_accent(ctx, incomplete):
|
||||||
|
preset_val = ctx.params.get("preset")
|
||||||
|
if preset_val:
|
||||||
|
try:
|
||||||
|
p_scheme, p_variant = resolve_preset(preset_val)
|
||||||
|
for v in list_schemes()[p_scheme].variants:
|
||||||
|
if v.id == p_variant:
|
||||||
|
return [a for a in v.accents if incomplete in a]
|
||||||
|
except (ValueError, KeyError):
|
||||||
|
pass
|
||||||
|
all_accents = set()
|
||||||
|
for meta in list_schemes().values():
|
||||||
|
for v in meta.variants:
|
||||||
|
all_accents.update(v.accents)
|
||||||
|
return [a for a in sorted(all_accents) if incomplete in a]
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def list_presets(
|
def list_presets(
|
||||||
json_format: bool = typer.Option(False, "--json", help="Output in JSON format"),
|
json_format: bool = typer.Option(False, "--json", help="Output in JSON format"),
|
||||||
@@ -30,7 +81,7 @@ def list_presets(
|
|||||||
for sid, meta in sorted(schemes.items()):
|
for sid, meta in sorted(schemes.items()):
|
||||||
variants = {}
|
variants = {}
|
||||||
for v in meta.variants:
|
for v in meta.variants:
|
||||||
entry = {"modes": sorted(v.modes)}
|
entry: dict[str, Any] = {"modes": sorted(v.modes)}
|
||||||
if v.accents:
|
if v.accents:
|
||||||
entry["accents"] = sorted(v.accents)
|
entry["accents"] = sorted(v.accents)
|
||||||
entry["default_accent"] = sorted(v.accents)[0]
|
entry["default_accent"] = sorted(v.accents)[0]
|
||||||
@@ -55,14 +106,35 @@ def list_presets(
|
|||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def generate(
|
def generate(
|
||||||
image_path: Optional[Path] = typer.Option(None, help="Path to source image. Required for image mode."),
|
image_path: Optional[Path] = typer.Option(
|
||||||
scheme: Optional[str] = typer.Option(
|
None, help="Path to source image. Required for image mode."
|
||||||
None, help="Color scheme algorithm to use for image mode. Ignored in preset mode."
|
),
|
||||||
|
scheme: Optional[str] = typer.Option(
|
||||||
|
None,
|
||||||
|
help="Color scheme algorithm to use for image mode. Ignored in preset mode.",
|
||||||
|
autocompletion=_complete_scheme_name,
|
||||||
|
),
|
||||||
|
preset: Optional[str] = typer.Option(
|
||||||
|
None,
|
||||||
|
help="Name of a premade scheme in this format: <scheme>:<variant>",
|
||||||
|
autocompletion=_complete_preset,
|
||||||
|
),
|
||||||
|
mode: Optional[str] = typer.Option(
|
||||||
|
None,
|
||||||
|
help="Mode of the preset scheme (dark or light).",
|
||||||
|
autocompletion=_complete_mode,
|
||||||
|
),
|
||||||
|
accent: Optional[str] = typer.Option(
|
||||||
|
None,
|
||||||
|
help="Accent for schemes that support it (e.g. mauve).",
|
||||||
|
autocompletion=_complete_accent,
|
||||||
),
|
),
|
||||||
preset: Optional[str] = typer.Option(None, help="Name of a premade scheme in this format: <scheme>:<variant>"),
|
|
||||||
mode: Optional[str] = typer.Option(None, help="Mode of the preset scheme (dark or light)."),
|
|
||||||
accent: Optional[str] = typer.Option(None, help="Accent for schemes that support it (e.g. mauve)."),
|
|
||||||
):
|
):
|
||||||
|
if not any([image_path, scheme, preset, mode, accent]):
|
||||||
|
print(
|
||||||
|
"Hint: use --preset <scheme>:<variant> or --image-path <path>",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
|
||||||
HOME = str(os.getenv("HOME"))
|
HOME = str(os.getenv("HOME"))
|
||||||
OUTPUT = Path(HOME + "/.local/state/zshell/scheme.json")
|
OUTPUT = Path(HOME + "/.local/state/zshell/scheme.json")
|
||||||
@@ -200,11 +272,15 @@ def generate(
|
|||||||
def harmonize(from_hct: Hct, to_hct: Hct, tone_boost: float) -> Hct:
|
def harmonize(from_hct: Hct, to_hct: Hct, tone_boost: float) -> Hct:
|
||||||
diff = difference_degrees(from_hct.hue, to_hct.hue)
|
diff = difference_degrees(from_hct.hue, to_hct.hue)
|
||||||
rotation = min(diff * 0.8, 100)
|
rotation = min(diff * 0.8, 100)
|
||||||
output_hue = sanitize_degrees_double(from_hct.hue + rotation * rotation_direction(from_hct.hue, to_hct.hue))
|
output_hue = sanitize_degrees_double(
|
||||||
|
from_hct.hue + rotation * rotation_direction(from_hct.hue, to_hct.hue)
|
||||||
|
)
|
||||||
tone = max(0.0, min(100.0, from_hct.tone * (1 + tone_boost)))
|
tone = max(0.0, min(100.0, from_hct.tone * (1 + tone_boost)))
|
||||||
return Hct.from_hct(output_hue, from_hct.chroma, tone)
|
return Hct.from_hct(output_hue, from_hct.chroma, tone)
|
||||||
|
|
||||||
def terminal_palette(colors: dict[str, str], mode: str, variant: str) -> dict[str, str]:
|
def terminal_palette(
|
||||||
|
colors: dict[str, str], mode: str, variant: str
|
||||||
|
) -> dict[str, str]:
|
||||||
light = mode.lower() == "light"
|
light = mode.lower() == "light"
|
||||||
|
|
||||||
key_hex = (
|
key_hex = (
|
||||||
@@ -236,7 +312,7 @@ def generate(
|
|||||||
|
|
||||||
image = Image.open(image_path)
|
image = Image.open(image_path)
|
||||||
image = image.convert("RGB")
|
image = image.convert("RGB")
|
||||||
image.thumbnail(size, Image.NEAREST)
|
image.thumbnail(size, Image.Resampling.NEAREST)
|
||||||
|
|
||||||
thumbnail_file.parent.mkdir(parents=True, exist_ok=True)
|
thumbnail_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
image.save(thumbnail_path, "JPEG")
|
image.save(thumbnail_path, "JPEG")
|
||||||
@@ -268,8 +344,15 @@ def generate(
|
|||||||
is_dark = ""
|
is_dark = ""
|
||||||
|
|
||||||
with Image.open(image_path) as img:
|
with Image.open(image_path) as img:
|
||||||
img.thumbnail((1, 1), Image.LANCZOS)
|
img.thumbnail((1, 1), Image.Resampling.LANCZOS)
|
||||||
hct = Hct.from_int(argb_from_rgb(*img.getpixel((0, 0))))
|
px = img.getpixel((0, 0))
|
||||||
|
if isinstance(px, (int, float)):
|
||||||
|
r = g = b = int(px)
|
||||||
|
elif px is not None:
|
||||||
|
r, g, b = int(px[0]), int(px[1]), int(px[2])
|
||||||
|
else:
|
||||||
|
r = g = b = 0
|
||||||
|
hct = Hct.from_int(argb_from_rgb(r, g, b))
|
||||||
is_dark = "light" if hct.tone > 50 else "dark"
|
is_dark = "light" if hct.tone > 50 else "dark"
|
||||||
|
|
||||||
return is_dark
|
return is_dark
|
||||||
@@ -431,6 +514,8 @@ def generate(
|
|||||||
|
|
||||||
raw = tpl_path.read_text(encoding="utf-8")
|
raw = tpl_path.read_text(encoding="utf-8")
|
||||||
out_path, body = split_directive_and_body(raw)
|
out_path, body = split_directive_and_body(raw)
|
||||||
|
if out_path is None:
|
||||||
|
continue
|
||||||
|
|
||||||
out_path.parent.mkdir(parents=True, exist_ok=True)
|
out_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
@@ -484,23 +569,30 @@ def generate(
|
|||||||
with CONFIG.open() as f:
|
with CONFIG.open() as f:
|
||||||
config = json.load(f)
|
config = json.load(f)
|
||||||
|
|
||||||
scheme = scheme or config["colors"]["schemeType"]
|
scheme_type = config["colors"].get("schemeType", "fruit-salad")
|
||||||
|
scheme = scheme or scheme_type
|
||||||
|
assert isinstance(scheme, str)
|
||||||
config_mode = config["general"]["color"]["mode"]
|
config_mode = config["general"]["color"]["mode"]
|
||||||
smart = bool(config["general"]["color"].get("smart", False))
|
smart = bool(config["general"]["color"].get("smart", False))
|
||||||
scheme_class = get_scheme_class(scheme)
|
scheme_class = get_scheme_class(scheme)
|
||||||
|
|
||||||
|
p_variant = "default"
|
||||||
if preset:
|
if preset:
|
||||||
p_scheme, p_variant = resolve_preset(preset)
|
p_scheme, p_variant = resolve_preset(preset)
|
||||||
schemes = list_schemes()
|
schemes = list_schemes()
|
||||||
if accent and p_scheme in schemes:
|
if accent and p_scheme in schemes:
|
||||||
meta = schemes[p_scheme]
|
meta = schemes[p_scheme]
|
||||||
var_accents = next((v.accents for v in meta.variants if v.id == p_variant), ())
|
var_accents = next(
|
||||||
|
(v.accents for v in meta.variants if v.id == p_variant), ()
|
||||||
|
)
|
||||||
if accent not in var_accents:
|
if accent not in var_accents:
|
||||||
available = ", ".join(var_accents) if var_accents else "none"
|
available = ", ".join(var_accents) if var_accents else "none"
|
||||||
raise typer.BadParameter(
|
raise typer.BadParameter(
|
||||||
f"Accent '{accent}' not available for '{p_scheme}:{p_variant}'. Available accents: {available}"
|
f"Accent '{accent}' not available for '{p_scheme}:{p_variant}'. Available accents: {available}"
|
||||||
)
|
)
|
||||||
palette_obj = get_palette(p_scheme, p_variant, mode or config_mode, accent=accent)
|
palette_obj = get_palette(
|
||||||
|
p_scheme, p_variant, mode or config_mode, accent=accent
|
||||||
|
)
|
||||||
colors = palette_obj.colors
|
colors = palette_obj.colors
|
||||||
effective_mode = palette_obj.mode
|
effective_mode = palette_obj.mode
|
||||||
name = palette_obj.scheme
|
name = palette_obj.scheme
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ def show():
|
|||||||
result = subprocess.run(args + ["ipc"] + ["show"], capture_output=True)
|
result = subprocess.run(args + ["ipc"] + ["show"], capture_output=True)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
raise click.ClickException(result.stderr.decode().strip())
|
raise click.ClickException(result.stderr.decode().strip())
|
||||||
|
sys.stdout.write(result.stdout.decode())
|
||||||
sys.stderr.write(result.stderr.decode())
|
sys.stderr.write(result.stderr.decode())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ def lockscreen(
|
|||||||
return
|
return
|
||||||
|
|
||||||
if size[0] < 3840 or size[1] < 2160:
|
if size[0] < 3840 or size[1] < 2160:
|
||||||
img = img.resize((size[0] // 2, size[1] // 2), Image.NEAREST)
|
img = img.resize((size[0] // 2, size[1] // 2), Image.Resampling.NEAREST)
|
||||||
else:
|
else:
|
||||||
img = img.resize((size[0] // 4, size[1] // 4), Image.NEAREST)
|
img = img.resize((size[0] // 4, size[1] // 4), Image.Resampling.NEAREST)
|
||||||
|
|
||||||
img = img.filter(ImageFilter.GaussianBlur(blur_amount))
|
img = img.filter(ImageFilter.GaussianBlur(blur_amount))
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
//@ pragma Env QT_SCALE_FACTOR_ROUNDING_POLICY=Round
|
//@ pragma Env QT_SCALE_FACTOR_ROUNDING_POLICY=Round
|
||||||
//@ pragma DropExpensiveFonts
|
//@ pragma DropExpensiveFonts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import qs.Extensions
|
|
||||||
import qs.Modules
|
import qs.Modules
|
||||||
import qs.Modules.Wallpaper
|
import qs.Modules.Wallpaper
|
||||||
import qs.Modules.Lock
|
import qs.Modules.Lock
|
||||||
@@ -39,7 +38,4 @@ ShellRoot {
|
|||||||
|
|
||||||
Polkit {
|
Polkit {
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadExtensions {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user