mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
settings: improved auto-nav to subtabs and highlight focus
This commit is contained in:
@@ -231,11 +231,12 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// Set sub-tab on the currently loaded tab content
|
||||
// Set sub-tab on the currently loaded tab content. Returns true if an NTabBar was found.
|
||||
function setSubTabIndex(subTabIndex) {
|
||||
if (activeTabContent) {
|
||||
setSubTabRecursive(activeTabContent, subTabIndex);
|
||||
return setSubTabRecursive(activeTabContent, subTabIndex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setSubTabRecursive(item, subTabIndex) {
|
||||
@@ -243,6 +244,16 @@ Item {
|
||||
return false;
|
||||
|
||||
if (item.objectName === "NTabBar") {
|
||||
// Prepare the sibling NTabView so the index change doesn't animate
|
||||
if (item.parent) {
|
||||
for (let j = 0; j < item.parent.children.length; j++) {
|
||||
const sibling = item.parent.children[j];
|
||||
if (sibling.objectName === "NTabView" && sibling.setIndexWithoutAnimation) {
|
||||
sibling.setIndexWithoutAnimation(subTabIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
item.currentIndex = subTabIndex;
|
||||
return true;
|
||||
}
|
||||
@@ -346,21 +357,21 @@ Item {
|
||||
if (root.activeTabContent && targetKey) {
|
||||
const widget = root.findAndHighlightWidget(root.activeTabContent, targetKey);
|
||||
if (widget && root.activeScrollView) {
|
||||
// Scroll widget into view
|
||||
const mapped = widget.mapToItem(root.activeScrollView.contentItem, 0, 0);
|
||||
const scrollBar = root.activeScrollView.ScrollBar.vertical;
|
||||
if (scrollBar) {
|
||||
const targetPos = (mapped.y - root.activeScrollView.height / 3) / root.activeScrollView.contentHeight;
|
||||
scrollBar.position = Math.max(0, Math.min(targetPos, 1.0 - scrollBar.size));
|
||||
}
|
||||
// Scroll widget into view using the Flickable directly
|
||||
const flickable = root.activeScrollView.contentItem;
|
||||
const mapped = widget.mapToItem(flickable.contentItem, 0, 0);
|
||||
const targetY = mapped.y - flickable.height / 3;
|
||||
flickable.contentY = Math.max(0, Math.min(targetY, flickable.contentHeight - flickable.height));
|
||||
|
||||
// Position highlight overlay
|
||||
// Position highlight overlay after scroll layout has settled
|
||||
Qt.callLater(function () {
|
||||
const overlayPos = widget.mapToItem(tabContentArea, 0, 0);
|
||||
highlightOverlay.x = overlayPos.x - Style.marginM;
|
||||
highlightOverlay.y = overlayPos.y - Style.marginM;
|
||||
highlightOverlay.width = widget.width + Style.marginM * 2;
|
||||
highlightOverlay.height = widget.height + Style.marginM * 2;
|
||||
highlightAnimation.restart();
|
||||
});
|
||||
}
|
||||
}
|
||||
targetKey = "";
|
||||
@@ -1226,13 +1237,13 @@ Item {
|
||||
item.screen = root.screen;
|
||||
}
|
||||
root.activeTabContent = item;
|
||||
if (root.highlightLabelKey) {
|
||||
if (root._pendingSubTab >= 0) {
|
||||
root.navigatingFromSearch = true;
|
||||
root.setSubTabIndex(root._pendingSubTab);
|
||||
root.navigatingFromSearch = false;
|
||||
if (root.setSubTabIndex(root._pendingSubTab))
|
||||
root._pendingSubTab = -1;
|
||||
root.navigatingFromSearch = false;
|
||||
}
|
||||
if (root.highlightLabelKey) {
|
||||
highlightScrollTimer.targetKey = root.highlightLabelKey;
|
||||
highlightScrollTimer.restart();
|
||||
}
|
||||
|
||||
@@ -165,12 +165,11 @@ SmartPanel {
|
||||
Qt.callLater(() => _settingsContent.navigateToResult(entry));
|
||||
} else {
|
||||
_settingsContent.requestedTab = requestedTab;
|
||||
_settingsContent.initialize();
|
||||
if (requestedSubTab >= 0) {
|
||||
const subTab = requestedSubTab;
|
||||
_settingsContent._pendingSubTab = requestedSubTab;
|
||||
requestedSubTab = -1;
|
||||
Qt.callLater(() => _settingsContent.navigateToTab(requestedTab, subTab));
|
||||
}
|
||||
_settingsContent.initialize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,13 +38,11 @@ FloatingWindow {
|
||||
Qt.callLater(() => settingsContent.navigateToResult(entry));
|
||||
} else {
|
||||
settingsContent.requestedTab = SettingsPanelService.requestedTab;
|
||||
settingsContent.initialize();
|
||||
if (SettingsPanelService.requestedSubTab >= 0) {
|
||||
const tab = SettingsPanelService.requestedTab;
|
||||
const subTab = SettingsPanelService.requestedSubTab;
|
||||
settingsContent._pendingSubTab = SettingsPanelService.requestedSubTab;
|
||||
SettingsPanelService.requestedSubTab = -1;
|
||||
Qt.callLater(() => settingsContent.navigateToTab(tab, subTab));
|
||||
}
|
||||
settingsContent.initialize();
|
||||
}
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ ColumnLayout {
|
||||
model: Quickshell.screens || []
|
||||
delegate: NBox {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: contentCol.implicitHeight + Style.marginL * 2
|
||||
implicitHeight: Math.round(contentCol.implicitHeight + Style.marginL * 2)
|
||||
color: Color.mSurface
|
||||
|
||||
property var brightnessMonitor: BrightnessService.getMonitorForScreen(modelData)
|
||||
|
||||
@@ -225,6 +225,11 @@ def resolve_tab_info(
|
||||
sub_label = subtab_labels[idx] if idx < len(subtab_labels) else None
|
||||
return tab_index, tab_label, idx, sub_label
|
||||
except ValueError:
|
||||
# File doesn't map to any subtab (e.g. a dialog). If the parent tab
|
||||
# has subtabs, the focus ring can't reach widgets inside dialogs, so
|
||||
# exclude them from the index.
|
||||
if subtab_names:
|
||||
return None, None, None, None
|
||||
return tab_index, tab_label, None, None
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import qs.Commons
|
||||
|
||||
Item {
|
||||
id: root
|
||||
objectName: "NTabView"
|
||||
|
||||
property int currentIndex: 0
|
||||
|
||||
@@ -29,6 +30,29 @@ Item {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
// Set the visible tab to idx without triggering a slide animation.
|
||||
// Call this BEFORE the bound currentIndex changes so that
|
||||
// onCurrentIndexChanged sees previousIndex === currentIndex and skips.
|
||||
function setIndexWithoutAnimation(idx) {
|
||||
fromXAnim.stop();
|
||||
fromOpacityAnim.stop();
|
||||
toXAnim.stop();
|
||||
toOpacityAnim.stop();
|
||||
animating = false;
|
||||
previousIndex = idx;
|
||||
for (let i = 0; i < contentItems.length; i++) {
|
||||
if (i === idx) {
|
||||
contentItems[i].x = 0;
|
||||
contentItems[i].visible = true;
|
||||
contentItems[i].opacity = 1.0;
|
||||
} else {
|
||||
contentItems[i].x = root.width;
|
||||
contentItems[i].visible = false;
|
||||
contentItems[i].opacity = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
_initializeItems();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user