完成全屏应用界面

This commit is contained in:
hewenfei 2023-03-17 17:25:50 +08:00
parent 12cae14527
commit 5754abb725
10 changed files with 486 additions and 25 deletions

View File

@ -0,0 +1,296 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQml.Models 2.12
import org.ukui.menu.core 1.0
import AppControls2 1.0 as AppControls2
RowLayout {
clip: true
Item {
Layout.preferredWidth: 90
Layout.fillHeight: true
ListView {
id: labelListView
signal labelClicked(string labelId)
anchors.centerIn: parent
width: parent.width
height: contentHeight > parent.height ? parent.height : contentHeight
interactive: contentHeight > parent.height
spacing: 5
model: DelegateModel {
groups: DelegateModelGroup {
name: "disabledItem"
includeByDefault: false
}
items.onChanged: {
for (let i = 0; i < items.count;) {
let item = items.get(i);
if (item.model.isDisable) {
items.setGroups(i, 1, "disabledItem");
continue;
}
++i;
}
}
model: modelManager.getLabelModel()
delegate: MouseArea {
width: ListView.view.width
height: 30
hoverEnabled: true
AppControls2.StyleBackground {
anchors.fill: parent
radius: 4
paletteRole: Palette.Base
alpha: parent.containsPress ? 0.36 : parent.containsMouse ? 0.18 : 0
useStyleTransparent: false
AppControls2.StyleText {
anchors.fill: parent
paletteRole: Palette.Text
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
text: model.displayName
}
}
onClicked: {
labelListView.labelClicked(model.id)
}
}
}
}
}
Item {
id: contentViewBase
Layout.fillWidth: true
Layout.fillHeight: true
property int maxColumns: 8
property int columns: {
let c = Math.floor(width / cellWidth);
return c > maxColumns ? maxColumns : c;
}
property int cellWidth: 220
// property int cellWidth: Math.min(Math.floor(width / columns), 220)
property int cellHeight: cellWidth
property int headerHeight: 30
Component.onCompleted: {
changeView(modelManager.getLabelGroupModel().containLabel);
modelManager.getLabelGroupModel().containLabelChanged.connect(changeView);
}
Component.onDestruction: {
modelManager.getLabelGroupModel().containLabelChanged.disconnect(changeView);
contentViewLoader.sourceComponent = undefined;
}
function changeView(toLabelView) {
contentViewLoader.sourceComponent = toLabelView ? labelViewComponent : appViewComponent;
}
Loader {
id: contentViewLoader
height: parent.height
width: contentViewBase.cellWidth * contentViewBase.columns + 2
anchors.horizontalCenter: parent.horizontalCenter
}
// view id: 1
Component {
id: appViewComponent
GridView {
ScrollBar.vertical: fullScreenScrollBar
cellWidth: contentViewBase.cellWidth
cellHeight: contentViewBase.cellHeight
model: modelManager.getAppModel()
delegate: Loader {
width: GridView.view.cellWidth
height: width
property int index: model.index
property int type: model.type
property string name: model.name
property string icon: model.icon
sourceComponent: {
if (type === DataType.Normal) {
return appComponent;
}
if (type === DataType.Folder) {
return folderComponent;
}
}
}
Component {
id: appComponent
MouseArea {
anchors.centerIn: parent
width: 100
height: 140
AppControls2.IconLabel {
anchors.fill: parent
iconWidth: 96
iconHeight: 96
appName: name
appIcon: icon
}
onClicked: {
parent.GridView.view.model.appClicked(index);
}
}
}
Component {
id: folderComponent
Item {
MouseArea {
anchors.centerIn: parent
width: 100; height: 140
AppControls2.IconLabel {
anchors.fill: parent
iconWidth: 96
iconHeight: 96
appName: name
appIcon: icon
}
}
}
}
}
}
// view id: 2
Component {
id: labelViewComponent
Flickable {
id: labelViewFlickable
ScrollBar.vertical: fullScreenScrollBar
contentHeight: labelColumn.height
flickableDirection: Flickable.VerticalFlick
interactive: !contentYAnimation.running
Column {
id: labelColumn
width: parent.width
height: childrenRect.height
Repeater {
id: labelRepeater
model: modelManager.getLabelGroupModel()
delegate: GridView {
id: labelAppsGridView
width: parent.width
height: contentHeight
property int labelIndex: index
interactive: false
cacheBuffer: 50
cellWidth: contentViewBase.cellWidth
cellHeight: contentViewBase.cellHeight
header: Row {
width: labelAppsGridView.width
height: contentViewBase.headerHeight
spacing: 15
Text {
id: labelName
anchors.verticalCenter: parent.verticalCenter
verticalAlignment: Text.AlignVCenter
width: contentWidth
text: name
}
AppControls2.StyleBackground {
anchors.verticalCenter: parent.verticalCenter
paletteRole: Palette.Light
height: 1
width: parent.width - labelName.width - parent.spacing
}
}
model: extraData
delegate: Item {
width: GridView.view.cellWidth
height: GridView.view.cellHeight
MouseArea {
anchors.centerIn: parent
width: 100; height: 140
AppControls2.IconLabel {
anchors.fill: parent
iconWidth: 96
iconHeight: 96
appName: modelData.name
appIcon: modelData.icon
}
onClicked: {
//console.log("clicked", Object.keys(model))
labelRepeater.model.openApp(labelAppsGridView.labelIndex, model.index);
}
}
}
}
}
}
NumberAnimation {
id: contentYAnimation
target: labelViewFlickable
property: "contentY"
duration: 300
onFinished: {
labelViewFlickable.returnToBounds();
}
}
function scrollerView(labelId) {
let labelindex = labelRepeater.model.getLabelIndex(labelId);
if (labelindex < 0 || labelindex >= labelRepeater.count) {
return;
}
let nextY = labelRepeater.itemAt(labelindex).y;
let sh = labelColumn.height - nextY;
if (sh < height) {
nextY -= (height - sh);
}
contentYAnimation.running = false;
if (nextY === contentY) {
return;
}
contentYAnimation.from = contentY;
contentYAnimation.to = nextY;
contentYAnimation.running = true;
}
Component.onCompleted: {
labelListView.labelClicked.connect(scrollerView);
}
Component.onDestruction: {
labelListView.labelClicked.disconnect(scrollerView);
}
}
}
}
Item {
Layout.preferredWidth: 90
Layout.fillHeight: true
AppControls2.ScrollBar {
id: fullScreenScrollBar
anchors.horizontalCenter: parent.horizontalCenter
height: parent.height
width: 20
}
}
}

View File

@ -0,0 +1,8 @@
import QtQuick 2.0
Item {
Text {
anchors.fill: parent
text: qsTr("FullScreenFooter.qml")
}
}

View File

@ -0,0 +1,32 @@
import QtQuick 2.0
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import AppControls2 1.0 as AppControls2
import org.ukui.menu.core 1.0
Item {
clip: true
PluginSelectMenu {
anchors.right: parent.right
anchors.rightMargin: 72 // 48+24
anchors.verticalCenter: parent.verticalCenter
width: 80
height: 48
spacing: 8
}
AppControls2.RoundButton {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
width: 48
height: 48
buttonIcon: "image://appicon/view-restore-symbolic"
onClicked: {
mainWindow.isFullScreen = false;
}
}
}

View File

@ -1,15 +1,28 @@
import QtQuick 2.0
import QtQuick.Layouts 1.12
Item {
Rectangle {
ColumnLayout {
anchors.fill: parent
color: "yellow"
anchors.leftMargin: 35
anchors.rightMargin: 35
MouseArea {
anchors.fill: parent;
onClicked: {
mainWindow.isFullScreen = !mainWindow.isFullScreen;
}
FullScreenHeader {
// full screen header
Layout.preferredHeight: 130
Layout.fillWidth: true
Layout.maximumHeight: 130
}
FullScreenContent {
// full screen content
Layout.fillHeight: true
Layout.fillWidth: true
}
FullScreenFooter {
// full screen footer
Layout.preferredHeight: 90
Layout.fillWidth: true
}
}
}

View File

@ -40,6 +40,7 @@ Item {
id: extensionListView
width: parent.width - 34
height: parent.height
clip: true
orientation: ListView.Horizontal
model: extensionManager.extensionModel()
delegate: headerDelegate
@ -60,6 +61,19 @@ Item {
Item {
width: 34
height: 34
Rectangle {
anchors.fill: parent
radius: height / 2
color: "lightblue"
MouseArea {
anchors.fill: parent
onClicked: {
//
console.log("==开始全屏==")
mainWindow.isFullScreen = true;
}
}
}
}
}

View File

@ -7,3 +7,6 @@ FullScreenUI 1.0 FullScreenUI.qml
AppPageHeader 1.0 AppPageHeader.qml
SearchInputBar 1.0 SearchInputBar.qml
PluginSelectMenu 1.0 PluginSelectMenu.qml
FullScreenHeader 1.0 FullScreenHeader.qml
FullScreenContent 1.0 FullScreenContent.qml
FullScreenFooter 1.0 FullScreenFooter.qml

View File

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2023, KylinSoft Co., Ltd.
*
* This program is free software: you can redistribute it and/or modify
@ -17,11 +17,98 @@
*/
import QtQuick 2.12
import QtQml 2.12
import AppUI 1.0 as AppUI
import AppControls2 1.0 as AppControls2
import org.ukui.menu.core 1.0
Item {
AppUI.NormalUI {
anchors.fill: parent
id: root
clip: true
property int animationDuration: 150
Component.onCompleted: {
loader.reloadUI();
mainWindow.beforeFullScreenChanged.connect(beforeFullScreenSlot);
mainWindow.fullScreenChanged.connect(fullScreenChangedSlot);
}
function fullScreenChangedSlot() {
loader.opacity = 0;
fullScreenAnimation.start();
}
function beforeFullScreenSlot(status) {
if (!status) {
loader.sourceComponent = undefined;
}
backgroundMask.alpha = status ? 0.5 : 0
backgroundMask.enableSizeChange = false;
}
ParallelAnimation {
id: fullScreenAnimation
NumberAnimation { target: backgroundMask; property: "width"; to: root.width; duration: root.animationDuration }
NumberAnimation { target: backgroundMask; property: "height"; to: root.height; duration: root.animationDuration }
onFinished: {
backgroundMask.enableSizeChange = true;
loader.reloadUI();
}
}
AppControls2.StyleBackground {
id: backgroundMask
anchors.left: parent.left
anchors.bottom: parent.bottom
alpha: 0
useStyleTransparent: false
paletteRole: Palette.Dark
property bool enableSizeChange: true
states: [
State {
name: "disableSizeChange"
when: !backgroundMask.enableSizeChange
AnchorChanges {
target: backgroundMask
anchors.top: undefined
anchors.right: undefined
}
},
State {
name: "enableSizeChange"
when: backgroundMask.enableSizeChange
AnchorChanges {
target: backgroundMask
anchors.top: parent.top
anchors.right: parent.right
}
}
]
Loader {
id: loader
anchors.fill: parent
Behavior on opacity {
NumberAnimation { duration: root.animationDuration }
}
function reloadUI() {
loader.sourceComponent = mainWindow.isFullScreen ? fullSceenComponent : normalComponent;
loader.opacity = 1;
}
}
}
Component {
id: fullSceenComponent
AppUI.FullScreenUI {}
}
Component {
id: normalComponent
AppUI.NormalUI {}
}
}

View File

@ -10,6 +10,9 @@
<file>AppUI/AppPage.qml</file>
<file>AppUI/Sidebar.qml</file>
<file>AppUI/AppList.qml</file>
<file>AppUI/FullScreenHeader.qml</file>
<file>AppUI/FullScreenContent.qml</file>
<file>AppUI/FullScreenFooter.qml</file>
<file>AppUI/AppPageHeader.qml</file>
<file>AppUI/SearchInputBar.qml</file>
<file>AppControls2/qmldir</file>

View File

@ -395,6 +395,8 @@ void MenuWindow::init()
QEvent event(QEvent::Move);
QCoreApplication::sendEvent(this, &event);
});
updateGeometry();
}
void MenuWindow::updateGeometry()
@ -412,13 +414,24 @@ bool MenuWindow::isFullScreen() const
return m_isFullScreen;
}
/**
* beforeFullScreenChanged -> (qml)onWidthChanged -> fullScreenChanged
* @param isFullScreen
*/
void MenuWindow::setFullScreen(bool isFullScreen)
{
if (m_isFullScreen == isFullScreen) {
return;
}
Q_EMIT beforeFullScreenChanged(isFullScreen);
m_isFullScreen = isFullScreen;
WindowHelper::windowBlur(this, !m_isFullScreen);
updateGeometry();
// 更新contentItem尺寸
QEvent event(QEvent::Resize);
QCoreApplication::sendEvent(this, &event);
@ -427,24 +440,17 @@ void MenuWindow::setFullScreen(bool isFullScreen)
void MenuWindow::exposeEvent(QExposeEvent *event)
{
if (isExposed()) {
requestActivate();
}
QQuickWindow::exposeEvent(event);
}
void MenuWindow::resizeEvent(QResizeEvent *event)
{
updateGeometry();
QQuickView::resizeEvent(event);
}
void MenuWindow::showEvent(QShowEvent *event)
{
QQuickWindow::showEvent(event);
}
void MenuWindow::focusOutEvent(QFocusEvent *event)
{
//setVisible(false);
// void QQuickWindow::focusOutEvent(QFocusEvent *ev) { Q_D(QQuickWindow); if (d->contentItem) d->contentItem->setFocus(false, ev->reason()); }
QQuickWindow::focusOutEvent(event);
// setVisible(false);
}
bool MenuWindow::event(QEvent *event)

View File

@ -115,14 +115,13 @@ public:
Q_SIGNALS:
void fullScreenChanged();
void beforeFullScreenChanged(bool isToFullScreen);
private Q_SLOTS:
void onActiveFocusItemChanged();
protected:
void exposeEvent(QExposeEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void showEvent(QShowEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;
bool event(QEvent *event) override;