mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
fead.添加了电源菜单
This commit is contained in:
165
LanMountainDesktop.PluginSdk/LICENSE
Normal file
165
LanMountainDesktop.PluginSdk/LICENSE
Normal file
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version of
|
||||
the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
@@ -13,6 +13,8 @@
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/wwiinnddyy/LanMountainDesktop</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageLicenseExpression>LGPL-3.0-or-later</PackageLicenseExpression>
|
||||
<Copyright>Copyright (c) LanMountainDesktop Contributors</Copyright>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
165
LanMountainDesktop.Shared.Contracts/LICENSE
Normal file
165
LanMountainDesktop.Shared.Contracts/LICENSE
Normal file
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version of
|
||||
the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
@@ -13,6 +13,8 @@
|
||||
<RepositoryUrl>https://github.com/wwiinnddyy/LanMountainDesktop</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
<PackageLicenseExpression>LGPL-3.0-or-later</PackageLicenseExpression>
|
||||
<Copyright>Copyright (c) LanMountainDesktop Contributors</Copyright>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.3.12" />
|
||||
|
||||
@@ -1087,5 +1087,23 @@
|
||||
"zhijiaohub.settings.auto_refresh_desc": "Automatically refresh the image list periodically.",
|
||||
"zhijiaohub.settings.interval": "Refresh Interval (minutes)",
|
||||
"zhijiaohub.settings.about": "About",
|
||||
"zhijiaohub.settings.about_desc": "ZhiJiaoHub displays interesting images from the educational technology community. Images are fetched from GitHub repositories and cached locally."
|
||||
"zhijiaohub.settings.about_desc": "ZhiJiaoHub displays interesting images from the educational technology community. Images are fetched from GitHub repositories and cached locally.",
|
||||
"power.menu": "Power",
|
||||
"power.title": "Power",
|
||||
"power.back": "Back",
|
||||
"power.shutdown": "Shutdown",
|
||||
"power.restart": "Restart",
|
||||
"power.logout": "Log Out",
|
||||
"power.sleep": "Sleep",
|
||||
"power.lock_screen": "Lock Screen",
|
||||
"power.shutdown_confirm_title": "Shutdown Confirmation",
|
||||
"power.shutdown_confirm_message": "Are you sure you want to shut down this computer? Unsaved data may be lost.",
|
||||
"power.restart_confirm_title": "Restart Confirmation",
|
||||
"power.restart_confirm_message": "Are you sure you want to restart this computer? Unsaved data may be lost.",
|
||||
"power.logout_confirm_title": "Log Out Confirmation",
|
||||
"power.logout_confirm_message": "Are you sure you want to log out?",
|
||||
"power.sleep_confirm_title": "Sleep Confirmation",
|
||||
"power.sleep_confirm_message": "Are you sure you want to put the computer to sleep?",
|
||||
"power.confirm_yes": "Yes",
|
||||
"power.confirm_cancel": "Cancel"
|
||||
}
|
||||
|
||||
@@ -1081,5 +1081,23 @@
|
||||
"zhijiaohub.settings.auto_refresh_desc": "定期自动刷新图片列表。",
|
||||
"zhijiaohub.settings.interval": "刷新间隔(分钟)",
|
||||
"zhijiaohub.settings.about": "关于",
|
||||
"zhijiaohub.settings.about_desc": "智教Hub 展示来自教育技术社区的有趣图片。图片从 GitHub 仓库获取并缓存在本地。"
|
||||
"zhijiaohub.settings.about_desc": "智教Hub 展示来自教育技术社区的有趣图片。图片从 GitHub 仓库获取并缓存在本地。",
|
||||
"power.menu": "电源",
|
||||
"power.title": "电源",
|
||||
"power.back": "返回",
|
||||
"power.shutdown": "关机",
|
||||
"power.restart": "重启",
|
||||
"power.logout": "注销",
|
||||
"power.sleep": "睡眠",
|
||||
"power.lock_screen": "锁定屏幕",
|
||||
"power.shutdown_confirm_title": "关机确认",
|
||||
"power.shutdown_confirm_message": "确定要关闭计算机吗?未保存的数据可能会丢失。",
|
||||
"power.restart_confirm_title": "重启确认",
|
||||
"power.restart_confirm_message": "确定要重启计算机吗?未保存的数据可能会丢失。",
|
||||
"power.logout_confirm_title": "注销确认",
|
||||
"power.logout_confirm_message": "确定要注销当前用户吗?",
|
||||
"power.sleep_confirm_title": "睡眠确认",
|
||||
"power.sleep_confirm_message": "确定要让计算机进入睡眠状态吗?",
|
||||
"power.confirm_yes": "确定",
|
||||
"power.confirm_cancel": "取消"
|
||||
}
|
||||
|
||||
253
LanMountainDesktop/Services/PowerManagementService.cs
Normal file
253
LanMountainDesktop/Services/PowerManagementService.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LanMountainDesktop.Services;
|
||||
|
||||
public interface IPowerManagementService
|
||||
{
|
||||
bool IsShutdownSupported { get; }
|
||||
bool IsRestartSupported { get; }
|
||||
bool IsLogoutSupported { get; }
|
||||
bool IsLockSupported { get; }
|
||||
bool IsSleepSupported { get; }
|
||||
|
||||
Task ShutdownAsync();
|
||||
Task RestartAsync();
|
||||
Task LogoutAsync();
|
||||
Task LockAsync();
|
||||
Task SleepAsync();
|
||||
|
||||
void ShowNativePowerUI(PowerAction action);
|
||||
}
|
||||
|
||||
public enum PowerAction
|
||||
{
|
||||
Shutdown,
|
||||
Restart
|
||||
}
|
||||
|
||||
public static class PowerManagementServiceFactory
|
||||
{
|
||||
private static IPowerManagementService? _instance;
|
||||
private static readonly object _lock = new();
|
||||
|
||||
public static IPowerManagementService GetOrCreate()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _instance ??= CreatePlatformService();
|
||||
}
|
||||
}
|
||||
|
||||
private static IPowerManagementService CreatePlatformService()
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
return new WindowsPowerManagementService();
|
||||
if (OperatingSystem.IsLinux())
|
||||
return new LinuxPowerManagementService();
|
||||
return new NullPowerManagementService();
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class WindowsPowerManagementService : IPowerManagementService
|
||||
{
|
||||
public bool IsShutdownSupported => true;
|
||||
public bool IsRestartSupported => true;
|
||||
public bool IsLogoutSupported => true;
|
||||
public bool IsLockSupported => true;
|
||||
public bool IsSleepSupported => true;
|
||||
|
||||
public async Task ShutdownAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "shutdown",
|
||||
Arguments = "/s /t 0",
|
||||
UseShellExecute = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async Task RestartAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "shutdown",
|
||||
Arguments = "/r /t 0",
|
||||
UseShellExecute = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async Task LogoutAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
ExitWindowsEx(0, 0);
|
||||
});
|
||||
}
|
||||
|
||||
public async Task LockAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
LockWorkStation();
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SleepAsync()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
SetSuspendState(false, false, false);
|
||||
});
|
||||
}
|
||||
|
||||
public void ShowNativePowerUI(PowerAction action)
|
||||
{
|
||||
var slideToShutDownPath = Environment.ExpandEnvironmentVariables(@"%windir%\System32\SlideToShutDown.exe");
|
||||
if (System.IO.File.Exists(slideToShutDownPath))
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = slideToShutDownPath,
|
||||
UseShellExecute = true
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case PowerAction.Shutdown:
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "shutdown",
|
||||
Arguments = "/s /t 5 /c \"LanMountainDesktop: Shutting down...\"",
|
||||
UseShellExecute = true
|
||||
});
|
||||
break;
|
||||
|
||||
case PowerAction.Restart:
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "shutdown",
|
||||
Arguments = "/r /t 5 /c \"LanMountainDesktop: Restarting...\"",
|
||||
UseShellExecute = true
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool ExitWindowsEx(uint uFlags, uint dwReason);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern void LockWorkStation();
|
||||
|
||||
[DllImport("powrprof.dll", SetLastError = true)]
|
||||
private static extern bool SetSuspendState(bool hibernate, bool forceCritical, bool disableWakeEvent);
|
||||
}
|
||||
|
||||
internal sealed class LinuxPowerManagementService : IPowerManagementService
|
||||
{
|
||||
public bool IsShutdownSupported => true;
|
||||
public bool IsRestartSupported => true;
|
||||
public bool IsLogoutSupported => true;
|
||||
public bool IsLockSupported => true;
|
||||
public bool IsSleepSupported => true;
|
||||
|
||||
public async Task ShutdownAsync()
|
||||
{
|
||||
await RunSystemctlCommand("poweroff -i");
|
||||
}
|
||||
|
||||
public async Task RestartAsync()
|
||||
{
|
||||
await RunSystemctlCommand("reboot -i");
|
||||
}
|
||||
|
||||
public async Task LogoutAsync()
|
||||
{
|
||||
await RunLoginctlCommand("terminate-session $XDG_SESSION_ID");
|
||||
}
|
||||
|
||||
public async Task LockAsync()
|
||||
{
|
||||
await RunLoginctlCommand("lock-session");
|
||||
}
|
||||
|
||||
public async Task SleepAsync()
|
||||
{
|
||||
await RunSystemctlCommand("suspend -i");
|
||||
}
|
||||
|
||||
public void ShowNativePowerUI(PowerAction action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case PowerAction.Shutdown:
|
||||
RunProcess("systemctl", "poweroff -i");
|
||||
break;
|
||||
case PowerAction.Restart:
|
||||
RunProcess("systemctl", "reboot -i");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task RunSystemctlCommand(string args)
|
||||
{
|
||||
await RunProcess("systemctl", args);
|
||||
}
|
||||
|
||||
private static async Task RunLoginctlCommand(string args)
|
||||
{
|
||||
await RunProcess("loginctl", args);
|
||||
}
|
||||
|
||||
private static async Task RunProcess(string command, string args)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = command,
|
||||
Arguments = args,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
})?.WaitForExit(5000);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppLogger.Error("LinuxPowerManagement", $"Failed to execute {command} {args}: {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class NullPowerManagementService : IPowerManagementService
|
||||
{
|
||||
public bool IsShutdownSupported => false;
|
||||
public bool IsRestartSupported => false;
|
||||
public bool IsLogoutSupported => false;
|
||||
public bool IsLockSupported => false;
|
||||
public bool IsSleepSupported => false;
|
||||
|
||||
public Task ShutdownAsync() => Task.CompletedTask;
|
||||
public Task RestartAsync() => Task.CompletedTask;
|
||||
public Task LogoutAsync() => Task.CompletedTask;
|
||||
public Task LockAsync() => Task.CompletedTask;
|
||||
public Task SleepAsync() => Task.CompletedTask;
|
||||
|
||||
public void ShowNativePowerUI(PowerAction action) { }
|
||||
}
|
||||
@@ -74,6 +74,10 @@ public partial class MainWindow
|
||||
Color PressedColor,
|
||||
Color DividerColor);
|
||||
|
||||
private readonly IPowerManagementService _powerService = PowerManagementServiceFactory.GetOrCreate();
|
||||
private bool _isPowerMenuOpen;
|
||||
private bool _isPowerMenuAnimating;
|
||||
|
||||
private void InitializeTaskbarProfileFlyout()
|
||||
{
|
||||
if (TaskbarProfileButton is null || TaskbarProfilePopup is null)
|
||||
@@ -98,6 +102,16 @@ public partial class MainWindow
|
||||
TaskbarProfileDisplayNameTextBlock.Text = profile.DisplayName;
|
||||
TaskbarProfileSettingsActionTextBlock.Text = L("tooltip.open_settings", "Settings");
|
||||
TaskbarProfileDesktopEditActionTextBlock.Text = L("button.component_library", "Edit Desktop");
|
||||
TaskbarProfilePowerActionTextBlock.Text = L("power.menu", "Power");
|
||||
TaskbarPowerTitleTextBlock.Text = L("power.title", "Power");
|
||||
TaskbarPowerBackTextBlock.Text = L("power.back", "Back");
|
||||
PowerShutdownTextBlock.Text = L("power.shutdown", "Shutdown");
|
||||
PowerRestartTextBlock.Text = L("power.restart", "Restart");
|
||||
PowerLogoutTextBlock.Text = L("power.logout", "Log Out");
|
||||
PowerSleepTextBlock.Text = L("power.sleep", "Sleep");
|
||||
PowerLockTextBlock.Text = L("power.lock_screen", "Lock Screen");
|
||||
|
||||
UpdatePowerMenuVisibility();
|
||||
ApplyTaskbarProfilePopupTheme(_appearanceThemeService.GetCurrent());
|
||||
|
||||
ToolTip.SetTip(TaskbarProfileButton, profile.DisplayName);
|
||||
@@ -216,6 +230,7 @@ public partial class MainWindow
|
||||
return;
|
||||
}
|
||||
|
||||
ResetPowerMenuState();
|
||||
RefreshTaskbarProfilePresentation();
|
||||
TaskbarProfilePopup.IsOpen = true;
|
||||
}
|
||||
@@ -279,6 +294,202 @@ public partial class MainWindow
|
||||
app?.OpenIndependentSettingsModule("MainWindowTaskbar");
|
||||
}
|
||||
|
||||
private void OnPowerMenuEnterClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_ = sender;
|
||||
_ = e;
|
||||
EnterPowerMenu();
|
||||
}
|
||||
|
||||
private void OnPowerMenuBackClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_ = sender;
|
||||
_ = e;
|
||||
ExitPowerMenu();
|
||||
}
|
||||
|
||||
private void ResetPowerMenuState()
|
||||
{
|
||||
_isPowerMenuOpen = false;
|
||||
_isPowerMenuAnimating = false;
|
||||
|
||||
if (TaskbarProfileMainPanel is not null)
|
||||
{
|
||||
TaskbarProfileMainPanel.IsVisible = true;
|
||||
TaskbarProfileMainPanel.Opacity = 1d;
|
||||
}
|
||||
|
||||
if (TaskbarProfilePowerPanel is not null)
|
||||
{
|
||||
TaskbarProfilePowerPanel.IsVisible = false;
|
||||
TaskbarProfilePowerPanel.Opacity = 0d;
|
||||
var transform = TaskbarProfilePowerPanel.RenderTransform as TranslateTransform;
|
||||
if (transform is not null) transform.X = 340d;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePowerMenuVisibility()
|
||||
{
|
||||
var supported = _powerService.IsShutdownSupported ||
|
||||
_powerService.IsRestartSupported ||
|
||||
_powerService.IsLogoutSupported ||
|
||||
_powerService.IsSleepSupported ||
|
||||
_powerService.IsLockSupported;
|
||||
|
||||
if (TaskbarProfilePowerActionButton is not null)
|
||||
{
|
||||
TaskbarProfilePowerActionButton.IsVisible = supported;
|
||||
}
|
||||
}
|
||||
|
||||
private async void EnterPowerMenu()
|
||||
{
|
||||
if (_isPowerMenuAnimating || _isPowerMenuOpen || TaskbarProfileMainPanel is null || TaskbarProfilePowerPanel is null)
|
||||
return;
|
||||
|
||||
_isPowerMenuAnimating = true;
|
||||
|
||||
TaskbarProfilePowerPanel.IsVisible = true;
|
||||
TaskbarProfilePowerPanel.Opacity = 0d;
|
||||
var powerTransform = TaskbarProfilePowerPanel.RenderTransform as TranslateTransform;
|
||||
if (powerTransform is not null) powerTransform.X = 340d;
|
||||
|
||||
await Task.Delay(16);
|
||||
|
||||
TaskbarProfileMainPanel.Opacity = 0d;
|
||||
TaskbarProfilePowerPanel.Opacity = 1d;
|
||||
if (powerTransform is not null) powerTransform.X = 0d;
|
||||
|
||||
await Task.Delay(280);
|
||||
|
||||
TaskbarProfileMainPanel.IsVisible = false;
|
||||
_isPowerMenuOpen = true;
|
||||
_isPowerMenuAnimating = false;
|
||||
}
|
||||
|
||||
private async void ExitPowerMenu()
|
||||
{
|
||||
if (_isPowerMenuAnimating || !_isPowerMenuOpen || TaskbarProfileMainPanel is null || TaskbarProfilePowerPanel is null)
|
||||
return;
|
||||
|
||||
_isPowerMenuAnimating = true;
|
||||
|
||||
TaskbarProfileMainPanel.IsVisible = true;
|
||||
TaskbarProfileMainPanel.Opacity = 0d;
|
||||
var powerTransform = TaskbarProfilePowerPanel.RenderTransform as TranslateTransform;
|
||||
if (powerTransform is not null) powerTransform.X = 0d;
|
||||
|
||||
await Task.Delay(16);
|
||||
|
||||
TaskbarProfileMainPanel.Opacity = 1d;
|
||||
TaskbarProfilePowerPanel.Opacity = 0d;
|
||||
if (powerTransform is not null) powerTransform.X = 340d;
|
||||
|
||||
await Task.Delay(280);
|
||||
|
||||
TaskbarProfilePowerPanel.IsVisible = false;
|
||||
_isPowerMenuOpen = false;
|
||||
_isPowerMenuAnimating = false;
|
||||
}
|
||||
|
||||
private async void OnPowerShutdownClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_ = sender;
|
||||
_ = e;
|
||||
ClosePopupIfOpen();
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
_powerService.ShowNativePowerUI(PowerAction.Shutdown);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ShowPowerConfirmDialogAsync(L("power.shutdown_confirm_title", "Shutdown"),
|
||||
L("power.shutdown_confirm_message", "Are you sure you want to shut down this computer?"),
|
||||
() => _powerService.ShutdownAsync());
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnPowerRestartClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_ = sender;
|
||||
_ = e;
|
||||
ClosePopupIfOpen();
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
_powerService.ShowNativePowerUI(PowerAction.Restart);
|
||||
}
|
||||
else
|
||||
{
|
||||
await ShowPowerConfirmDialogAsync(L("power.restart_confirm_title", "Restart"),
|
||||
L("power.restart_confirm_message", "Are you sure you want to restart this computer?"),
|
||||
() => _powerService.RestartAsync());
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnPowerLogoutClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_ = sender;
|
||||
_ = e;
|
||||
ClosePopupIfOpen();
|
||||
|
||||
await ShowPowerConfirmDialogAsync(L("power.logout_confirm_title", "Log Out"),
|
||||
L("power.logout_confirm_message", "Are you sure you want to log out?"),
|
||||
() => _powerService.LogoutAsync());
|
||||
}
|
||||
|
||||
private async void OnPowerSleepClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_ = sender;
|
||||
_ = e;
|
||||
ClosePopupIfOpen();
|
||||
|
||||
await ShowPowerConfirmDialogAsync(L("power.sleep_confirm_title", "Sleep"),
|
||||
L("power.sleep_confirm_message", "Are you sure you want to put the computer to sleep?"),
|
||||
() => _powerService.SleepAsync());
|
||||
}
|
||||
|
||||
private async void OnPowerLockClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_ = sender;
|
||||
_ = e;
|
||||
ClosePopupIfOpen();
|
||||
await _powerService.LockAsync();
|
||||
}
|
||||
|
||||
private async Task ShowPowerConfirmDialogAsync(string title, string message, Func<Task> action)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dialog = new ContentDialog
|
||||
{
|
||||
Title = title,
|
||||
Content = message,
|
||||
PrimaryButtonText = L("power.confirm_yes", "Yes"),
|
||||
SecondaryButtonText = L("power.confirm_cancel", "Cancel")
|
||||
};
|
||||
|
||||
var result = await dialog.ShowAsync(this);
|
||||
if (result == ContentDialogResult.Primary)
|
||||
{
|
||||
await action();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppLogger.Error("PowerMenu", $"Dialog error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ClosePopupIfOpen()
|
||||
{
|
||||
if (TaskbarProfilePopup is not null && TaskbarProfilePopup.IsOpen)
|
||||
{
|
||||
TaskbarProfilePopup.IsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloseComponentLibraryClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
_componentLibraryWindowService.Close(this);
|
||||
|
||||
@@ -398,69 +398,215 @@
|
||||
<Border x:Name="TaskbarProfilePopupPanel"
|
||||
Classes="taskbar-profile-popup-panel"
|
||||
Margin="0,0,0,10">
|
||||
<StackPanel Width="280"
|
||||
Spacing="12">
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="12">
|
||||
<Border x:Name="TaskbarProfileHeaderAvatarBorder"
|
||||
Classes="taskbar-profile-popup-avatar"
|
||||
Width="44"
|
||||
Height="44"
|
||||
ClipToBounds="True">
|
||||
<Grid>
|
||||
<Image x:Name="TaskbarProfileHeaderAvatarImage"
|
||||
Stretch="UniformToFill"
|
||||
IsVisible="False" />
|
||||
<TextBlock x:Name="TaskbarProfileHeaderAvatarFallbackText"
|
||||
Classes="taskbar-profile-popup-primary"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="SemiBold"
|
||||
Text="U" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<Grid Width="340">
|
||||
<Grid x:Name="TaskbarProfileMainPanel"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.Transitions>
|
||||
<Transitions>
|
||||
<DoubleTransition Property="Opacity" Duration="{StaticResource FluttermotionToken.Duration.Fast}" />
|
||||
</Transitions>
|
||||
</Grid.Transitions>
|
||||
<StackPanel Spacing="12">
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="12">
|
||||
<Border x:Name="TaskbarProfileHeaderAvatarBorder"
|
||||
Classes="taskbar-profile-popup-avatar"
|
||||
Width="44"
|
||||
Height="44"
|
||||
ClipToBounds="True">
|
||||
<Grid>
|
||||
<Image x:Name="TaskbarProfileHeaderAvatarImage"
|
||||
Stretch="UniformToFill"
|
||||
IsVisible="False" />
|
||||
<TextBlock x:Name="TaskbarProfileHeaderAvatarFallbackText"
|
||||
Classes="taskbar-profile-popup-primary"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="SemiBold"
|
||||
Text="U" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="2">
|
||||
<TextBlock x:Name="TaskbarProfileDisplayNameTextBlock"
|
||||
Classes="taskbar-profile-popup-title"
|
||||
Text="User" />
|
||||
<StackPanel Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="2">
|
||||
<TextBlock x:Name="TaskbarProfileDisplayNameTextBlock"
|
||||
Classes="taskbar-profile-popup-title"
|
||||
Text="User" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Border x:Name="TaskbarProfilePopupDivider"
|
||||
Height="1"
|
||||
Background="{DynamicResource TaskbarProfilePopupDividerBrush}" />
|
||||
|
||||
<Button x:Name="TaskbarProfileSettingsActionButton"
|
||||
Classes="taskbar-profile-popup-action"
|
||||
Click="OnOpenSettingsClick">
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="12">
|
||||
<mi:MaterialIcon Classes="taskbar-profile-popup-action-icon"
|
||||
Kind="Settings" />
|
||||
<TextBlock x:Name="TaskbarProfileSettingsActionTextBlock"
|
||||
Grid.Column="1"
|
||||
Classes="taskbar-profile-popup-action-text"
|
||||
Text="Settings" />
|
||||
</Grid>
|
||||
</Button>
|
||||
|
||||
<Button x:Name="TaskbarProfileDesktopEditActionButton"
|
||||
Classes="taskbar-profile-popup-action"
|
||||
Click="OnOpenComponentLibraryClick">
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="12">
|
||||
<mi:MaterialIcon Classes="taskbar-profile-popup-action-icon"
|
||||
Kind="Pencil" />
|
||||
<TextBlock x:Name="TaskbarProfileDesktopEditActionTextBlock"
|
||||
Grid.Column="1"
|
||||
Classes="taskbar-profile-popup-action-text"
|
||||
Text="Edit Desktop" />
|
||||
</Grid>
|
||||
</Button>
|
||||
|
||||
<Button x:Name="TaskbarProfilePowerActionButton"
|
||||
Classes="taskbar-profile-popup-action"
|
||||
Click="OnPowerMenuEnterClick">
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="12">
|
||||
<mi:MaterialIcon Classes="taskbar-profile-popup-action-icon"
|
||||
Kind="Power" />
|
||||
<TextBlock x:Name="TaskbarProfilePowerActionTextBlock"
|
||||
Grid.Column="1"
|
||||
Classes="taskbar-profile-popup-action-text"
|
||||
Text="Power" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<Border x:Name="TaskbarProfilePopupDivider"
|
||||
Height="1"
|
||||
Background="{DynamicResource TaskbarProfilePopupDividerBrush}" />
|
||||
<Grid x:Name="TaskbarProfilePowerPanel"
|
||||
IsVisible="False"
|
||||
Opacity="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<Grid.Transitions>
|
||||
<Transitions>
|
||||
<DoubleTransition Property="Opacity" Duration="{StaticResource FluttermotionToken.Duration.Fast}" />
|
||||
</Transitions>
|
||||
</Grid.Transitions>
|
||||
<Grid.RenderTransform>
|
||||
<TranslateTransform>
|
||||
<TranslateTransform.Transitions>
|
||||
<Transitions>
|
||||
<DoubleTransition Property="X"
|
||||
Duration="{StaticResource FluttermotionToken.Duration.Fast}"
|
||||
Easing="0.22,1,0.36,1" />
|
||||
</Transitions>
|
||||
</TranslateTransform.Transitions>
|
||||
</TranslateTransform>
|
||||
</Grid.RenderTransform>
|
||||
<StackPanel Spacing="8">
|
||||
<Button x:Name="TaskbarPowerBackButton"
|
||||
Padding="4,6"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Foreground="{DynamicResource AdaptiveTextPrimaryBrush}"
|
||||
HorizontalAlignment="Left"
|
||||
Click="OnPowerMenuBackClick">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<fi:SymbolIcon Classes="icon-s"
|
||||
Symbol="ArrowLeft"
|
||||
IconVariant="Regular" />
|
||||
<TextBlock x:Name="TaskbarPowerBackTextBlock"
|
||||
VerticalAlignment="Center"
|
||||
Text="Back" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button x:Name="TaskbarProfileSettingsActionButton"
|
||||
Classes="taskbar-profile-popup-action"
|
||||
Click="OnOpenSettingsClick">
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="12">
|
||||
<mi:MaterialIcon Classes="taskbar-profile-popup-action-icon"
|
||||
Kind="Settings" />
|
||||
<TextBlock x:Name="TaskbarProfileSettingsActionTextBlock"
|
||||
Grid.Column="1"
|
||||
Classes="taskbar-profile-popup-action-text"
|
||||
Text="Settings" />
|
||||
</Grid>
|
||||
</Button>
|
||||
<TextBlock x:Name="TaskbarPowerTitleTextBlock"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource TaskbarProfilePopupTextBrush}"
|
||||
Margin="2,6,0,0"
|
||||
Text="Power" />
|
||||
|
||||
<Button x:Name="TaskbarProfileDesktopEditActionButton"
|
||||
Classes="taskbar-profile-popup-action"
|
||||
Click="OnOpenComponentLibraryClick">
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="12">
|
||||
<mi:MaterialIcon Classes="taskbar-profile-popup-action-icon"
|
||||
Kind="Pencil" />
|
||||
<TextBlock x:Name="TaskbarProfileDesktopEditActionTextBlock"
|
||||
Grid.Column="1"
|
||||
Classes="taskbar-profile-popup-action-text"
|
||||
Text="Edit Desktop" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<Border Height="1"
|
||||
Background="{DynamicResource TaskbarProfilePopupDividerBrush}"
|
||||
Margin="0,4" />
|
||||
|
||||
<Button x:Name="PowerShutdownButton"
|
||||
Classes="taskbar-profile-popup-action"
|
||||
Click="OnPowerShutdownClick">
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="14">
|
||||
<mi:MaterialIcon Classes="taskbar-profile-popup-action-icon"
|
||||
Kind="Power" />
|
||||
<TextBlock x:Name="PowerShutdownTextBlock"
|
||||
Grid.Column="1"
|
||||
Classes="taskbar-profile-popup-action-text"
|
||||
Text="Shutdown" />
|
||||
</Grid>
|
||||
</Button>
|
||||
|
||||
<Button x:Name="PowerRestartButton"
|
||||
Classes="taskbar-profile-popup-action"
|
||||
Click="OnPowerRestartClick">
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="14">
|
||||
<mi:MaterialIcon Classes="taskbar-profile-popup-action-icon"
|
||||
Kind="Refresh" />
|
||||
<TextBlock x:Name="PowerRestartTextBlock"
|
||||
Grid.Column="1"
|
||||
Classes="taskbar-profile-popup-action-text"
|
||||
Text="Restart" />
|
||||
</Grid>
|
||||
</Button>
|
||||
|
||||
<Button x:Name="PowerLogoutButton"
|
||||
Classes="taskbar-profile-popup-action"
|
||||
Click="OnPowerLogoutClick">
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="14">
|
||||
<mi:MaterialIcon Classes="taskbar-profile-popup-action-icon"
|
||||
Kind="ExitToApp" />
|
||||
<TextBlock x:Name="PowerLogoutTextBlock"
|
||||
Grid.Column="1"
|
||||
Classes="taskbar-profile-popup-action-text"
|
||||
Text="Log Out" />
|
||||
</Grid>
|
||||
</Button>
|
||||
|
||||
<Button x:Name="PowerSleepButton"
|
||||
Classes="taskbar-profile-popup-action"
|
||||
Click="OnPowerSleepClick">
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="14">
|
||||
<mi:MaterialIcon Classes="taskbar-profile-popup-action-icon"
|
||||
Kind="WeatherNight" />
|
||||
<TextBlock x:Name="PowerSleepTextBlock"
|
||||
Grid.Column="1"
|
||||
Classes="taskbar-profile-popup-action-text"
|
||||
Text="Sleep" />
|
||||
</Grid>
|
||||
</Button>
|
||||
|
||||
<Button x:Name="PowerLockButton"
|
||||
Classes="taskbar-profile-popup-action"
|
||||
Click="OnPowerLockClick">
|
||||
<Grid ColumnDefinitions="Auto,*"
|
||||
ColumnSpacing="14">
|
||||
<mi:MaterialIcon Classes="taskbar-profile-popup-action-icon"
|
||||
Kind="Lock" />
|
||||
<TextBlock x:Name="PowerLockTextBlock"
|
||||
Grid.Column="1"
|
||||
Classes="taskbar-profile-popup-action-text"
|
||||
Text="Lock Screen" />
|
||||
</Grid>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Popup>
|
||||
</Grid>
|
||||
|
||||
Reference in New Issue
Block a user