feat: 设置窗口网卡列表样式增强
- 增加网卡类型图标、hover 背景高亮、选中行浅蓝底+蓝边框 - 修复 RadioButton text 在 dde-shell/DTK 下渲染异常导致接口名不可见, 改用独立 Text 元素渲染接口名 - 行高 36->30、间距 4->2 收紧列表布局 - 网卡超过 5 行时 ScrollView 垂直滚动,滚动条样式与弹窗一致 - 行内所有元素垂直居中对齐 - 整行点击即可切换网卡,RadioButton 替换为自绘圆点指示器 - 选中态颜色加深为 #1565c0 提升图标对比度
This commit is contained in:
@@ -58,6 +58,22 @@ Window {
|
|||||||
return qsTr("其他")
|
return qsTr("其他")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据接口名返回类型图标(Unicode 符号),用于网络接口列表每行左侧
|
||||||
|
// 设计原因:项目未引入图标库,用 Text 渲染 Unicode 符号(与 networkview.qml 的 ↓↑ 一致);
|
||||||
|
// 所选符号均为 DejaVu/Noto 等常见字体覆盖的单色字形,避免彩色 emoji 破坏浅色主题观感,
|
||||||
|
// 匹配规则与 interfaceDescription 保持一致
|
||||||
|
function interfaceIcon(name) {
|
||||||
|
if (name === "lo") return "↻" // 本地回环:循环箭头
|
||||||
|
if (name === "Meta") return "▢" // 虚拟接口:空心方块
|
||||||
|
if (/^enp|^eth/.test(name)) return "⇄" // 有线网络:双向链路
|
||||||
|
if (/^wlp|^wlan/.test(name)) return "∿" // 无线网络:信号波形
|
||||||
|
if (/^docker|^veth/.test(name)) return "▣" // 容器网络:盒中盒
|
||||||
|
if (/^br/.test(name)) return "⋈" // 桥接:连接(蝴蝶结形)
|
||||||
|
if (/^tun|^tap/.test(name)) return "⚿" // VPN:钥匙
|
||||||
|
if (/^virbr/.test(name)) return "⋈" // 虚拟桥接:同桥接
|
||||||
|
return "◉" // 其他:通用网络节点
|
||||||
|
}
|
||||||
|
|
||||||
// 显示时居中到桌面
|
// 显示时居中到桌面
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
@@ -159,59 +175,157 @@ Window {
|
|||||||
color: "#666666"
|
color: "#666666"
|
||||||
}
|
}
|
||||||
|
|
||||||
// 接口列表容器
|
// 接口列表容器:最多显示约 5 行(160px),超出部分垂直滚动
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.preferredHeight: networkInterfaces.length > 0 ? 40 + networkInterfaces.length * 28 : 80
|
// 高度 = 上下边距 10*2 + 列表可视高度;可视高度 = min(内容高度, 160)
|
||||||
|
// 内容高度 = 行高 30*n + 行间距 2*(n-1),接口少时卡片随行数收缩
|
||||||
|
Layout.preferredHeight: networkInterfaces.length > 0
|
||||||
|
? Math.min(ifaceColumn.implicitHeight, 160) + 20
|
||||||
|
: 80
|
||||||
color: "#ffffff"
|
color: "#ffffff"
|
||||||
radius: 10
|
radius: 10
|
||||||
border.width: 1
|
border.width: 1
|
||||||
border.color: "#e0e0e0"
|
border.color: "#e0e0e0"
|
||||||
|
|
||||||
ColumnLayout {
|
// 用 ScrollView 而非裸 Flickable:自带滚轮滚动支持,
|
||||||
|
// 且滚动条显示时自动让出内容宽度,不会遮挡行内文字
|
||||||
|
ScrollView {
|
||||||
|
id: ifaceScrollView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 12
|
anchors.margins: 10
|
||||||
spacing: 8
|
visible: networkInterfaces.length > 0
|
||||||
|
clip: true
|
||||||
|
// 内容宽度跟随可视宽度(自动减去滚动条占位),禁止水平滚动
|
||||||
|
contentWidth: availableWidth
|
||||||
|
|
||||||
// 接口列表:RadioButton 单选切换活动接口
|
// 自定义滚动条样式:与 networkview.qml chip 滚动条一致(4px 圆角矩形),
|
||||||
|
// 颜色改用硬编码灰色系适配本窗口浅色主题;
|
||||||
|
// AsNeeded 策略保证接口少时不显示滚动条
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
id: ifaceScrollBar
|
||||||
|
policy: ScrollBar.AsNeeded
|
||||||
|
interactive: true
|
||||||
|
|
||||||
|
contentItem: Rectangle {
|
||||||
|
implicitWidth: 4
|
||||||
|
radius: 2
|
||||||
|
// 按压/悬停加深、默认稍淡,保证滚动条在白色卡片上可见
|
||||||
|
color: ifaceScrollBar.pressed ? "#666666"
|
||||||
|
: (ifaceScrollBar.hovered ? "#666666" : "#999999")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: ifaceColumn
|
||||||
|
width: ifaceScrollView.availableWidth
|
||||||
|
// 行间距 2:紧凑列表,各行 hover 背景不粘连
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
// 接口列表:整行点击切换活动接口
|
||||||
|
// 行样式:类型图标 + 单选圆点 + 名称 + 右侧类型描述;
|
||||||
|
// hover 显示浅灰底,选中行浅蓝底 + 蓝色细边框
|
||||||
Repeater {
|
Repeater {
|
||||||
model: networkInterfaces
|
model: networkInterfaces
|
||||||
delegate: Rectangle {
|
delegate: Rectangle {
|
||||||
Layout.fillWidth: true
|
id: ifaceRow
|
||||||
Layout.preferredHeight: 28
|
width: ifaceColumn.width
|
||||||
color: "transparent"
|
height: 30
|
||||||
radius: 6
|
radius: 8
|
||||||
|
// 当前行是否为活动接口(选中态)
|
||||||
|
readonly property bool isActive: modelData === activeInterface
|
||||||
|
readonly property bool hovered: rowMouse.containsMouse
|
||||||
|
// 选中态强调色用 #1565c0(较 #1976d2 更深):
|
||||||
|
// 保证 ▢ 等空心图标在浅蓝选中背景上仍有足够对比度
|
||||||
|
readonly property color activeColor: "#1565c0"
|
||||||
|
// 背景优先级:选中 > hover;选中行 hover 时稍加深以保留交互反馈
|
||||||
|
color: isActive
|
||||||
|
? (hovered ? "#d8ecfd" : "#e3f2fd")
|
||||||
|
: (hovered ? "#f0f0f0" : "transparent")
|
||||||
|
// 选中行加蓝色细边框增强视觉;未选中保持透明边框占位,避免切换时出现 1px 抖动
|
||||||
|
border.width: 1
|
||||||
|
border.color: isActive ? "#90caf9" : "transparent"
|
||||||
|
|
||||||
RadioButton {
|
// 整行 hover + 点击:行内元素均不处理鼠标事件,
|
||||||
anchors.left: parent.left
|
// 点击行任意位置即选中该接口
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
MouseArea {
|
||||||
text: modelData
|
id: rowMouse
|
||||||
checked: modelData === activeInterface
|
anchors.fill: parent
|
||||||
onToggled: {
|
hoverEnabled: true
|
||||||
if (applet) applet.setActiveInterface(modelData)
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: if (applet) applet.setActiveInterface(modelData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
// 网卡类型图标:固定宽度居中,保证不同字形的视觉对齐
|
||||||
|
Text {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredWidth: 20
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: interfaceIcon(modelData)
|
||||||
|
font.pixelSize: 14
|
||||||
|
color: ifaceRow.isActive ? ifaceRow.activeColor : "#777777"
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自绘单选圆点:替代 RadioButton 作纯视觉指示器,
|
||||||
|
// 避免 DTK 样式渲染差异(本文件早期版本 RadioButton 标签渲染异常),
|
||||||
|
// 选中态颜色完全可控;点击由整行 MouseArea 统一处理
|
||||||
|
Rectangle {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.preferredWidth: 16
|
||||||
|
Layout.preferredHeight: 16
|
||||||
|
radius: 8
|
||||||
|
color: "transparent"
|
||||||
|
border.width: 2
|
||||||
|
border.color: ifaceRow.isActive ? ifaceRow.activeColor
|
||||||
|
: (ifaceRow.hovered ? "#666666" : "#999999")
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 8
|
||||||
|
height: 8
|
||||||
|
radius: 4
|
||||||
|
visible: ifaceRow.isActive
|
||||||
|
color: ifaceRow.activeColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 接口名称:过长时右侧省略
|
||||||
|
Text {
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: modelData
|
||||||
|
font.pixelSize: 13
|
||||||
|
font.weight: ifaceRow.isActive ? Font.Medium : Font.Normal
|
||||||
|
color: ifaceRow.isActive ? ifaceRow.activeColor : "#333333"
|
||||||
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
|
|
||||||
// 接口类型描述:右对齐显示,与接口名分居两侧
|
// 接口类型描述:右对齐显示,与接口名分居两侧
|
||||||
Text {
|
Text {
|
||||||
anchors.right: parent.right
|
Layout.alignment: Qt.AlignVCenter
|
||||||
anchors.rightMargin: 12
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
text: interfaceDescription(modelData)
|
text: interfaceDescription(modelData)
|
||||||
font.pixelSize: 12
|
font.pixelSize: 12
|
||||||
color: "#999999"
|
color: "#999999"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 无接口提示
|
// 无接口提示:在卡片内居中
|
||||||
Text {
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
text: qsTr("未检测到网络接口")
|
text: qsTr("未检测到网络接口")
|
||||||
font.pixelSize: 12
|
font.pixelSize: 12
|
||||||
color: "#999999"
|
color: "#999999"
|
||||||
visible: networkInterfaces.length === 0
|
visible: networkInterfaces.length === 0
|
||||||
Layout.alignment: Qt.AlignHCenter
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user