1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-03 02:52:07 -04:00

Compare commits

..

11 Commits

Author SHA1 Message Date
bbedward
12e91534eb niri: empty input region & disable spotlight content when not open 2025-11-20 16:44:46 -05:00
bbedward
d9da88ceb5 niri: embed spotlight to same window as overview layer 2025-11-20 16:28:26 -05:00
bbedward
2dbfec0307 niri: close spotlight when closing overview 2025-11-20 13:56:35 -05:00
Lucas
09cf8c9641 niri: add spotlight on overview typing functionality (#774) 2025-11-20 13:48:30 -05:00
Pi Home Server
f1bed4d6a3 Feature/privacy widget fix (#772)
* Fix active camera icon

* Fix active camera icon
2025-11-20 12:30:23 -05:00
bbedward
2ed6c33c83 missing import 2025-11-19 19:14:47 -05:00
bbedward
7ad532ed17 dankinstall: add ultramarine 2025-11-19 18:53:41 -05:00
bbedward
92fe8c5b14 hyprland: restore focus grab to tray menus 2025-11-19 17:24:14 -05:00
bbedward
8e95572589 modals: move HyprFocusGrab out of common Modal 2025-11-19 17:16:51 -05:00
bbedward
62da862a66 modal: round textureSize pixels 2025-11-19 14:36:08 -05:00
bbedward
993e34f548 dankinstall: weakdeps for niri/system 2025-11-19 09:35:22 -05:00
23 changed files with 421 additions and 10 deletions

View File

@@ -19,10 +19,12 @@ func init() {
Register("fedora-asahi-remix", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
return NewFedoraDistribution(config, logChan)
})
Register("bluefin", "#0B57A4", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
return NewFedoraDistribution(config, logChan)
})
Register("ultramarine", "#00078b", FamilyFedora, func(config DistroConfig, logChan chan<- string) Distribution {
return NewFedoraDistribution(config, logChan)
})
}
type FedoraDistribution struct {
@@ -506,6 +508,14 @@ func (f *FedoraDistribution) installDNFPackages(ctx context.Context, packages []
f.log(fmt.Sprintf("Installing DNF packages: %s", strings.Join(packages, ", ")))
args := []string{"dnf", "install", "-y"}
for _, pkg := range packages {
if pkg == "niri" || pkg == "niri-git" {
args = append(args, "--setopt=install_weak_deps=False")
break
}
}
args = append(args, packages...)
progressChan <- InstallProgressMsg{

View File

@@ -171,6 +171,7 @@ Singleton {
property string spotlightModalViewMode: "list"
property bool sortAppsAlphabetically: false
property int appLauncherGridColumns: 4
property bool spotlightCloseNiriOverview: true
property string weatherLocation: "New York, NY"
property string weatherCoordinates: "40.7128,-74.0060"

View File

@@ -97,6 +97,7 @@ var SPEC = {
spotlightModalViewMode: { def: "list" },
sortAppsAlphabetically: { def: false },
appLauncherGridColumns: { def: 4 },
spotlightCloseNiriOverview: { def: true },
weatherLocation: { def: "New York, NY" },
weatherCoordinates: { def: "40.7128,-74.0060" },

View File

@@ -22,7 +22,7 @@ import qs.Modules.ProcessList
import qs.Modules.Settings
import qs.Modules.DankBar
import qs.Modules.DankBar.Popouts
import qs.Modules.HyprWorkspaces
import qs.Modules.WorkspaceOverlays
import qs.Modules.Plugins
import qs.Services
@@ -581,4 +581,12 @@ Item {
id: hyprlandOverview
}
}
LazyLoader {
id: niriOverviewOverlayLoader
active: CompositorService.isNiri
component: NiriOverviewOverlay {
id: niriOverviewOverlay
}
}
}

View File

@@ -1,4 +1,6 @@
import QtQuick
import Quickshell
import Quickshell.Hyprland
import qs.Common
import qs.Modals.Common
import qs.Services
@@ -9,6 +11,11 @@ DankModal {
layerNamespace: "dms:bluetooth-pairing"
HyprlandFocusGrab {
windows: [root]
active: CompositorService.isHyprland && root.shouldHaveFocus
}
property string deviceName: ""
property string deviceAddress: ""
property string requestType: ""

View File

@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Hyprland
import Quickshell.Io
import qs.Common
import qs.Modals.Common
@@ -14,6 +15,11 @@ DankModal {
layerNamespace: "dms:clipboard"
HyprlandFocusGrab {
windows: [clipboardHistoryModal]
active: CompositorService.isHyprland && clipboardHistoryModal.shouldHaveFocus
}
property int totalCount: 0
property var clipboardEntries: []
property string searchText: ""

View File

@@ -95,11 +95,6 @@ PanelWindow {
return WlrKeyboardFocus.Exclusive
}
HyprlandFocusGrab {
windows: [root]
active: CompositorService.isHyprland && shouldHaveFocus
}
onVisibleChanged: {
if (root.visible) {
opened()
@@ -245,7 +240,7 @@ PanelWindow {
clip: false
layer.enabled: true
layer.smooth: false
layer.textureSize: Qt.size(width * root.dpr, height * root.dpr)
layer.textureSize: Qt.size(Math.round(width * root.dpr), Math.round(height * root.dpr))
opacity: root.shouldBeVisible ? 1 : 0
scale: modalContainer.scaleValue
x: Theme.snap(modalContainer.animX + (parent.width - width) * (1 - modalContainer.scaleValue) * 0.5, root.dpr)

View File

@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Hyprland
import Quickshell.Io
import qs.Common
import qs.Modals.Common
@@ -12,6 +13,11 @@ DankModal {
layerNamespace: "dms:color-picker"
HyprlandFocusGrab {
windows: [root]
active: CompositorService.isHyprland && root.shouldHaveFocus
}
property string pickerTitle: "Choose Color"
property color selectedColor: SessionData.recentColors.length > 0 ? SessionData.recentColors[0] : Theme.primary
property var onColorSelectedCallback: null

View File

@@ -2,10 +2,13 @@ import Qt.labs.folderlistmodel
import QtCore
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Hyprland
import Quickshell.Io
import qs.Common
import qs.Modals.Common
import qs.Modals.FileBrowser
import qs.Services
import qs.Widgets
DankModal {
@@ -13,6 +16,11 @@ DankModal {
layerNamespace: "dms:file-browser"
HyprlandFocusGrab {
windows: [fileBrowserModal]
active: CompositorService.isHyprland && fileBrowserModal.shouldHaveFocus
}
keepPopoutsOpen: true
property string homeDir: StandardPaths.writableLocation(StandardPaths.HomeLocation)

View File

@@ -1,4 +1,6 @@
import QtQuick
import Quickshell
import Quickshell.Hyprland
import Quickshell.Io
import qs.Common
import qs.Modals.Common
@@ -11,6 +13,11 @@ DankModal {
layerNamespace: "dms:notification-center-modal"
HyprlandFocusGrab {
windows: [notificationModal]
active: CompositorService.isHyprland && notificationModal.shouldHaveFocus
}
property bool notificationModalOpen: false
property var notificationListRef: null

View File

@@ -1,4 +1,6 @@
import QtQuick
import Quickshell
import Quickshell.Hyprland
import qs.Common
import qs.Modals.Common
import qs.Services
@@ -9,6 +11,11 @@ DankModal {
layerNamespace: "dms:polkit"
HyprlandFocusGrab {
windows: [root]
active: CompositorService.isHyprland && root.shouldHaveFocus
}
property string passwordInput: ""
property var currentFlow: PolkitService.agent?.flow
property bool isLoading: false

View File

@@ -1,5 +1,6 @@
import QtQuick
import Quickshell
import Quickshell.Hyprland
import qs.Common
import qs.Modals.Common
import qs.Services
@@ -10,6 +11,11 @@ DankModal {
layerNamespace: "dms:power-menu"
HyprlandFocusGrab {
windows: [root]
active: CompositorService.isHyprland && root.shouldHaveFocus
}
property int selectedIndex: 0
property int selectedRow: 0
property int selectedCol: 0

View File

@@ -1,5 +1,7 @@
import QtQuick
import QtQuick.Effects
import Quickshell
import Quickshell.Hyprland
import Quickshell.Io
import qs.Common
import qs.Modals.Common
@@ -13,6 +15,11 @@ DankModal {
layerNamespace: "dms:settings"
HyprlandFocusGrab {
windows: [settingsModal]
active: CompositorService.isHyprland && settingsModal.shouldHaveFocus
}
property Component settingsContent
property alias profileBrowser: profileBrowser
property int currentTabIndex: 0

View File

@@ -158,6 +158,10 @@ Item {
onAppLaunched: () => {
if (parentModal)
parentModal.hide()
if (SettingsData.spotlightCloseNiriOverview && NiriService.inOverview) {
NiriService.toggleOverview()
}
}
onViewModeSelected: mode => {
SettingsData.set("spotlightModalViewMode", mode)
@@ -170,6 +174,10 @@ Item {
onFileOpened: () => {
if (parentModal)
parentModal.hide()
if (SettingsData.spotlightCloseNiriOverview && NiriService.inOverview) {
NiriService.toggleOverview()
}
}
}

View File

@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Hyprland
import Quickshell.Io
import Quickshell.Widgets
import qs.Common
@@ -14,10 +15,17 @@ DankModal {
layerNamespace: "dms:spotlight"
HyprlandFocusGrab {
windows: [spotlightModal]
active: CompositorService.isHyprland && spotlightModal.shouldHaveFocus
}
property bool spotlightOpen: false
property alias spotlightContent: spotlightContentInstance
property bool openedFromOverview: false
function show() {
openedFromOverview = false
spotlightOpen = true
open()
@@ -49,6 +57,7 @@ DankModal {
}
function hide() {
openedFromOverview = false
spotlightOpen = false
close()
}

View File

@@ -1,4 +1,6 @@
import QtQuick
import Quickshell
import Quickshell.Hyprland
import qs.Common
import qs.Modals.Common
import qs.Services
@@ -9,6 +11,11 @@ DankModal {
layerNamespace: "dms:wifi-password"
HyprlandFocusGrab {
windows: [root]
active: CompositorService.isHyprland && root.shouldHaveFocus
}
property string wifiPasswordSSID: ""
property string wifiPasswordInput: ""
property string wifiUsernameInput: ""

View File

@@ -591,6 +591,72 @@ Item {
}
}
StyledRect {
width: parent.width
height: niriOverviewSection.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.2)
border.width: 0
visible: CompositorService.isNiri
Column {
id: niriOverviewSection
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
Row {
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "open_in_new"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Close Overview on Launch")
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
- niriOverviewToggle.width - Theme.spacingM * 3
height: 1
}
DankToggle {
id: niriOverviewToggle
width: 32
height: 18
checked: SettingsData.spotlightCloseNiriOverview
anchors.verticalCenter: parent.verticalCenter
onToggled: checked => {
SettingsData.set("spotlightCloseNiriOverview", checked)
}
}
}
StyledText {
width: parent.width
text: I18n.tr("When enabled, launching an app from the launcher will automatically close the Niri overview if it's open.")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
}
}
}
StyledRect {
width: parent.width
height: recentlyUsedSection.implicitHeight + Theme.spacingL * 2

View File

@@ -0,0 +1,220 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import qs.Common
import qs.Modals.Spotlight
import qs.Services
Scope {
id: niriOverviewScope
property bool searchActive: false
property string searchActiveScreen: ""
property bool overlayActive: NiriService.inOverview && !(PopoutService.spotlightModal?.spotlightOpen ?? false)
function showSpotlight(screenName) {
searchActive = true
searchActiveScreen = screenName
}
function hideSpotlight() {
searchActive = false
searchActiveScreen = ""
}
Connections {
target: NiriService
function onInOverviewChanged() {
if (!NiriService.inOverview) {
hideSpotlight()
} else {
searchActive = false
searchActiveScreen = ""
}
}
function onCurrentOutputChanged() {
if (NiriService.inOverview && searchActive && searchActiveScreen !== "" && searchActiveScreen !== NiriService.currentOutput) {
hideSpotlight()
}
}
}
Loader {
id: niriOverlayLoader
active: overlayActive
asynchronous: false
sourceComponent: Variants {
id: overlayVariants
model: Quickshell.screens
PanelWindow {
id: overlayWindow
required property var modelData
readonly property real dpr: CompositorService.getScreenScale(screen)
readonly property bool isActiveScreen: screen.name === NiriService.currentOutput
readonly property bool shouldShowSpotlight: niriOverviewScope.searchActive && screen.name === niriOverviewScope.searchActiveScreen
screen: modelData
visible: NiriService.inOverview
color: "transparent"
WlrLayershell.namespace: "dms:niri-overview-spotlight"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: {
if (!NiriService.inOverview) return WlrKeyboardFocus.None
if (!isActiveScreen) return WlrKeyboardFocus.None
return WlrKeyboardFocus.Exclusive
}
mask: Region {
item: shouldShowSpotlight ? spotlightContainer : null
}
onShouldShowSpotlightChanged: {
if (!shouldShowSpotlight && isActiveScreen) {
Qt.callLater(() => keyboardFocusScope.forceActiveFocus())
}
}
anchors {
top: true
left: true
right: true
bottom: true
}
FocusScope {
id: keyboardFocusScope
anchors.fill: parent
focus: true
Keys.onPressed: event => {
if (!overlayWindow.shouldShowSpotlight) {
if ([Qt.Key_Escape, Qt.Key_Return].includes(event.key)) {
NiriService.toggleOverview()
event.accepted = true
return
}
if (event.key === Qt.Key_Left) {
NiriService.moveColumnLeft()
event.accepted = true
return
}
if (event.key === Qt.Key_Right) {
NiriService.moveColumnRight()
event.accepted = true
return
}
if (event.key === Qt.Key_Up) {
NiriService.moveWorkspaceUp()
event.accepted = true
return
}
if (event.key === Qt.Key_Down) {
NiriService.moveWorkspaceDown()
event.accepted = true
return
}
if (event.modifiers & (Qt.ControlModifier | Qt.MetaModifier) || [Qt.Key_Delete, Qt.Key_Backspace].includes(event.key)) {
event.accepted = false
return
}
if (!event.isAutoRepeat && event.text) {
niriOverviewScope.showSpotlight(overlayWindow.screen.name)
if (spotlightContent?.searchField) {
spotlightContent.searchField.text = event.text.trim()
if (spotlightContent.appLauncher) {
spotlightContent.appLauncher.searchQuery = event.text.trim()
}
Qt.callLater(() => spotlightContent.searchField.forceActiveFocus())
}
event.accepted = true
}
}
}
}
Item {
id: spotlightContainer
x: Theme.snap((parent.width - width) / 2, overlayWindow.dpr)
y: Theme.snap((parent.height - height) / 2, overlayWindow.dpr)
width: Theme.px(500, overlayWindow.dpr)
height: Theme.px(600, overlayWindow.dpr)
property real scaleValue: 0.96
scale: scaleValue
opacity: overlayWindow.shouldShowSpotlight ? 1 : 0
enabled: overlayWindow.shouldShowSpotlight
layer.enabled: true
layer.smooth: false
layer.textureSize: Qt.size(Math.round(width * overlayWindow.dpr), Math.round(height * overlayWindow.dpr))
Connections {
target: overlayWindow
function onShouldShowSpotlightChanged() {
spotlightContainer.scaleValue = overlayWindow.shouldShowSpotlight ? 1.0 : 0.96
}
}
Behavior on scaleValue {
NumberAnimation {
duration: Theme.expressiveDurations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: niriOverviewScope.searchActive ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.expressiveDurations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: niriOverviewScope.searchActive ? Theme.expressiveCurves.expressiveDefaultSpatial : Theme.expressiveCurves.emphasized
}
}
Rectangle {
anchors.fill: parent
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
radius: Theme.cornerRadius
border.color: Theme.outlineMedium
border.width: 1
}
SpotlightContent {
id: spotlightContent
anchors.fill: parent
anchors.margins: 0
property var fakeParentModal: QtObject {
property bool spotlightOpen: overlayWindow.shouldShowSpotlight
function hide() {
niriOverviewScope.hideSpotlight()
if (overlayWindow.isActiveScreen) {
Qt.callLater(() => keyboardFocusScope.forceActiveFocus())
}
}
}
Component.onCompleted: {
parentModal = fakeParentModal
}
}
}
}
}
}
}

View File

@@ -606,6 +606,38 @@ Singleton {
})
}
function moveColumnLeft() {
return send({
"Action": {
"FocusColumnLeft": {}
}
})
}
function moveColumnRight() {
return send({
"Action": {
"FocusColumnRight": {}
}
})
}
function moveWorkspaceDown() {
return send({
"Action": {
"FocusWorkspaceDown": {}
}
})
}
function moveWorkspaceUp() {
return send({
"Action": {
"FocusWorkspaceUp": {}
}
})
}
function switchToWorkspace(workspaceIndex) {
return send({
"Action": {

View File

@@ -34,7 +34,7 @@ Singleton {
}
PwObjectTracker {
objects: Pipewire.nodes.values.filter(node => node.audio && !node.isStream)
objects: Pipewire.nodes.values.filter(node => !node.isStream)
}
readonly property bool cameraActive: {
@@ -47,7 +47,7 @@ Singleton {
if (!node || !node.ready) {
continue
}
if (node.properties && node.properties["media.class"] === "Stream/Input/Video") {
if (node.properties["stream.is-live"] === "true") {
return true