Add basic app menu
This commit is contained in:
49
debian/changelog
vendored
49
debian/changelog
vendored
@@ -1,3 +1,52 @@
|
||||
vesperos-taskbar (26.3) vesperos; urgency=medium
|
||||
|
||||
* Add Windows 10-style Start Menu replacing the GNOME app grid popup.
|
||||
- Clicking the Show Apps button now opens a floating Win10-style menu
|
||||
instead of the GNOME overview app grid.
|
||||
- Includes a search bar that filters installed applications in real time.
|
||||
- "Pinned" view shows favourites + running apps in a 4-column icon grid.
|
||||
- "All apps" view shows an alphabetically sorted, scrollable app list
|
||||
with letter dividers.
|
||||
- Footer row shows the current user name and a power button with a
|
||||
submenu for Lock, Sign out, Sleep, Restart, and Shut down.
|
||||
- Menu is positioned above/beside the Show Apps button depending on
|
||||
panel side; clamped to monitor bounds on multi-monitor setups.
|
||||
- Closes on Escape key, outside click, or when the GNOME overview opens.
|
||||
- Right-click context menu on the Show Apps button is unchanged.
|
||||
|
||||
-- VesperOS Desktop Team <contact@oxmc.me> Sat, 04 Apr 2026 12:00:00 +0000
|
||||
|
||||
vesperos-taskbar (26.2) vesperos; urgency=medium
|
||||
|
||||
* Re-based on upstream version 73.1 (additional bug fixes).
|
||||
* Fix crash in activateFirstWindow when window list is empty.
|
||||
* Fix memory leaks: properly destroy menus in TaskbarAppIcon and
|
||||
ShowAppsIconWrapper on extension disable.
|
||||
* Fix extension enable stalling when Zorin Dash is already disabled.
|
||||
* Fix intellihide panel visibility logic to avoid unintended hide.
|
||||
* Fix overview disable to end hotkey preview cycle before teardown.
|
||||
* Fix null-dereference on intellihide reference in panel toggle handler.
|
||||
* Fix show-desktop button removal to clean up pending timeout and
|
||||
restore hidden workspace state before destroying the button.
|
||||
* Fix panelManager cleanup of boxPointer signal ID on disable.
|
||||
* Fix Workspace prototype not restored when extension is disabled
|
||||
while the overview spread is active.
|
||||
* Fix panel.outerSize reference to panel.geom.outerSize.
|
||||
* Guard _newLookingGlassResize against missing primary monitor panel.
|
||||
* Fix taskbar destroy to clean up _onStageKeyPress override when the
|
||||
overview is open at disable time.
|
||||
* Fix workspace signal disconnect to tolerate removed dynamic workspaces.
|
||||
* Fix menu-state-changed signal connected before item container exists.
|
||||
* Fix animateWindowOpacity reading initialOpacity before window
|
||||
actor reassignment.
|
||||
* Fix ColorUtils RGB-to-HSV conversion variable shadowing bug.
|
||||
* Fix windowPreview workspace.activate to always restore _shouldAnimate
|
||||
via try/finally.
|
||||
* Fix windowPreview set_child_at_index with null parent guard and
|
||||
index clamping.
|
||||
|
||||
-- VesperOS Desktop Team <contact@oxmc.me> Fri, 03 Apr 2026 15:30:00 +0000
|
||||
|
||||
vesperos-taskbar (26.1) vesperos; urgency=medium
|
||||
|
||||
* VesperOS fork of Zorin OS's gnome-shell-extension-zorin-taskbar package.
|
||||
|
||||
2
debian/rules
vendored
2
debian/rules
vendored
@@ -59,6 +59,8 @@ override_dh_install:
|
||||
install -m 644 $$lang_dir/LC_MESSAGES/gtk4-ding.mo \
|
||||
$(PKG_DIR)/usr/share/locale/$$lang/LC_MESSAGES/; \
|
||||
done
|
||||
# --- Start Menu themes ---
|
||||
cp -r $(CURDIR)/src/themes $(EXT_DIR)/themes
|
||||
|
||||
override_dh_fixperms:
|
||||
dh_fixperms
|
||||
|
||||
@@ -854,5 +854,10 @@
|
||||
<default>''</default>
|
||||
<summary>The preferences page name to display</summary>
|
||||
</key>
|
||||
<key type="s" name="start-menu-stylesheet">
|
||||
<default>''</default>
|
||||
<summary>Custom stylesheet for the Start Menu</summary>
|
||||
<description>Path to a CSS file to load on top of the built-in Start Menu styles. Leave empty to use defaults.</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
|
||||
742
src/startMenu.js
Normal file
742
src/startMenu.js
Normal file
@@ -0,0 +1,742 @@
|
||||
/*
|
||||
* This file is part of the VesperOS system-taskbar extension.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
import Clutter from 'gi://Clutter'
|
||||
import Gio from 'gi://Gio'
|
||||
import GLib from 'gi://GLib'
|
||||
import GObject from 'gi://GObject'
|
||||
import Pango from 'gi://Pango'
|
||||
import Shell from 'gi://Shell'
|
||||
import St from 'gi://St'
|
||||
|
||||
import * as AppFavorites from 'resource:///org/gnome/shell/ui/appFavorites.js'
|
||||
import * as Main from 'resource:///org/gnome/shell/ui/main.js'
|
||||
import * as Util from 'resource:///org/gnome/shell/misc/util.js'
|
||||
import { gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js'
|
||||
|
||||
import * as Utils from './utils.js'
|
||||
import { SETTINGS } from './extension.js'
|
||||
|
||||
const MENU_WIDTH = 460
|
||||
const MENU_HEIGHT = 560
|
||||
const APP_ICON_SIZE = 48
|
||||
const GRID_COLUMNS = 4
|
||||
const CELL_SIZE = Math.floor(MENU_WIDTH / GRID_COLUMNS) // 115
|
||||
|
||||
export const Win10StartMenu = GObject.registerClass(
|
||||
class Win10StartMenu extends St.Widget {
|
||||
_init(dtpPanel) {
|
||||
super._init({
|
||||
style_class: 'win10-start-menu',
|
||||
reactive: true,
|
||||
visible: false,
|
||||
width: MENU_WIDTH,
|
||||
height: MENU_HEIGHT,
|
||||
})
|
||||
|
||||
this._dtpPanel = dtpPanel
|
||||
this._appSystem = Shell.AppSystem.get_default()
|
||||
this.isOpen = false
|
||||
this._captureId = 0
|
||||
this._focusId = 0
|
||||
this._sourceActor = null
|
||||
this._customStylesheet = null
|
||||
this._powerPopup = null
|
||||
this._timeoutsHandler = new Utils.TimeoutsHandler()
|
||||
|
||||
this._buildUI()
|
||||
Main.uiGroup.add_child(this)
|
||||
|
||||
this._signalsHandler = new Utils.GlobalSignalsHandler()
|
||||
this._signalsHandler.add(
|
||||
[Main.overview, 'showing', () => { if (this.isOpen) this.close() }],
|
||||
[this._appSystem, 'installed-changed', () => {
|
||||
this._allAppsList.remove_all_children()
|
||||
}],
|
||||
[SETTINGS, 'changed::start-menu-stylesheet', () => this._reloadStylesheet()],
|
||||
)
|
||||
|
||||
this._reloadStylesheet()
|
||||
}
|
||||
|
||||
_reloadStylesheet() {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme()
|
||||
|
||||
if (this._customStylesheet) {
|
||||
theme.unload_stylesheet(this._customStylesheet)
|
||||
this._customStylesheet = null
|
||||
}
|
||||
|
||||
let path = SETTINGS.get_string('start-menu-stylesheet').trim()
|
||||
if (path) {
|
||||
let file = Gio.File.new_for_path(path)
|
||||
if (file.query_exists(null)) {
|
||||
this._customStylesheet = file
|
||||
theme.load_stylesheet(this._customStylesheet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_buildUI() {
|
||||
let outer = new St.BoxLayout({
|
||||
style_class: 'win10-start-menu-inner',
|
||||
vertical: true,
|
||||
reactive: true,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
})
|
||||
|
||||
// ── Search bar ──────────────────────────────────────────────────────────
|
||||
this._searchEntry = new St.Entry({
|
||||
style_class: 'win10-search-entry',
|
||||
hint_text: _('Search apps'),
|
||||
can_focus: true,
|
||||
x_expand: true,
|
||||
})
|
||||
this._searchEntry.clutter_text.connect(
|
||||
'text-changed',
|
||||
() => this._onSearchTextChanged(),
|
||||
)
|
||||
outer.add_child(this._searchEntry)
|
||||
|
||||
// ── Content stack (pinned / all-apps / search results) ──────────────────
|
||||
this._stack = new St.Widget({
|
||||
layout_manager: new Clutter.BinLayout(),
|
||||
y_expand: true,
|
||||
x_expand: true,
|
||||
})
|
||||
|
||||
this._pinnedView = this._buildPinnedView()
|
||||
this._allAppsView = this._buildAllAppsView()
|
||||
this._searchView = this._buildSearchView()
|
||||
this._allAppsView.visible = false
|
||||
this._searchView.visible = false
|
||||
|
||||
this._stack.add_child(this._pinnedView)
|
||||
this._stack.add_child(this._allAppsView)
|
||||
this._stack.add_child(this._searchView)
|
||||
outer.add_child(this._stack)
|
||||
|
||||
// ── Footer ──────────────────────────────────────────────────────────────
|
||||
outer.add_child(this._buildFooter())
|
||||
this._outer = outer
|
||||
this.add_child(outer)
|
||||
}
|
||||
|
||||
_buildPinnedView() {
|
||||
let view = new St.BoxLayout({
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
})
|
||||
|
||||
let header = new St.BoxLayout({ style_class: 'win10-section-header' })
|
||||
header.add_child(
|
||||
new St.Label({
|
||||
text: _('Pinned'),
|
||||
style_class: 'win10-section-label',
|
||||
x_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
}),
|
||||
)
|
||||
let allAppsBtn = new St.Button({
|
||||
label: _('All apps ›'),
|
||||
style_class: 'win10-link-button',
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
can_focus: true,
|
||||
})
|
||||
allAppsBtn.connect('clicked', () => this._showView('all'))
|
||||
header.add_child(allAppsBtn)
|
||||
view.add_child(header)
|
||||
|
||||
let scroll = new St.ScrollView({
|
||||
style_class: 'win10-apps-scroll',
|
||||
hscrollbar_policy: St.PolicyType.NEVER,
|
||||
vscrollbar_policy: St.PolicyType.AUTOMATIC,
|
||||
y_expand: true,
|
||||
x_expand: true,
|
||||
})
|
||||
this._pinnedGrid = new St.BoxLayout({
|
||||
style_class: 'win10-app-grid',
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
})
|
||||
scroll.add_child(this._pinnedGrid)
|
||||
view.add_child(scroll)
|
||||
return view
|
||||
}
|
||||
|
||||
_buildAllAppsView() {
|
||||
let view = new St.BoxLayout({
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
})
|
||||
|
||||
let header = new St.BoxLayout({ style_class: 'win10-section-header' })
|
||||
|
||||
let backBtn = new St.Button({
|
||||
style_class: 'win10-link-button',
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
can_focus: true,
|
||||
})
|
||||
let backBox = new St.BoxLayout({ y_align: Clutter.ActorAlign.CENTER })
|
||||
backBox.add_child(
|
||||
new St.Icon({
|
||||
icon_name: 'go-previous-symbolic',
|
||||
icon_size: 14,
|
||||
style_class: 'win10-back-icon',
|
||||
}),
|
||||
)
|
||||
backBox.add_child(
|
||||
new St.Label({ text: _('Back'), y_align: Clutter.ActorAlign.CENTER }),
|
||||
)
|
||||
backBtn.set_child(backBox)
|
||||
backBtn.connect('clicked', () => this._showView('pinned'))
|
||||
header.add_child(backBtn)
|
||||
header.add_child(
|
||||
new St.Label({
|
||||
text: _('All apps'),
|
||||
style_class: 'win10-section-label',
|
||||
x_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
}),
|
||||
)
|
||||
view.add_child(header)
|
||||
|
||||
let scroll = new St.ScrollView({
|
||||
style_class: 'win10-apps-scroll',
|
||||
hscrollbar_policy: St.PolicyType.NEVER,
|
||||
vscrollbar_policy: St.PolicyType.AUTOMATIC,
|
||||
y_expand: true,
|
||||
x_expand: true,
|
||||
})
|
||||
this._allAppsList = new St.BoxLayout({ vertical: true, x_expand: true })
|
||||
scroll.add_child(this._allAppsList)
|
||||
view.add_child(scroll)
|
||||
return view
|
||||
}
|
||||
|
||||
_buildSearchView() {
|
||||
let view = new St.BoxLayout({
|
||||
vertical: true,
|
||||
x_expand: true,
|
||||
y_expand: true,
|
||||
})
|
||||
view.add_child(
|
||||
new St.Label({
|
||||
text: _('Search results'),
|
||||
style_class: 'win10-section-label',
|
||||
style: 'margin: 8px 12px 4px;',
|
||||
}),
|
||||
)
|
||||
let scroll = new St.ScrollView({
|
||||
style_class: 'win10-apps-scroll',
|
||||
hscrollbar_policy: St.PolicyType.NEVER,
|
||||
vscrollbar_policy: St.PolicyType.AUTOMATIC,
|
||||
y_expand: true,
|
||||
x_expand: true,
|
||||
})
|
||||
this._searchList = new St.BoxLayout({ vertical: true, x_expand: true })
|
||||
scroll.add_child(this._searchList)
|
||||
view.add_child(scroll)
|
||||
return view
|
||||
}
|
||||
|
||||
_buildFooter() {
|
||||
let footer = new St.BoxLayout({
|
||||
style_class: 'win10-footer',
|
||||
x_expand: true,
|
||||
})
|
||||
|
||||
// User button (left)
|
||||
let userBtn = new St.Button({
|
||||
style_class: 'win10-footer-button',
|
||||
x_expand: true,
|
||||
x_align: Clutter.ActorAlign.START,
|
||||
can_focus: true,
|
||||
})
|
||||
let userBox = new St.BoxLayout()
|
||||
userBox.add_child(
|
||||
new St.Icon({
|
||||
icon_name: 'avatar-default-symbolic',
|
||||
icon_size: 20,
|
||||
style_class: 'win10-footer-icon',
|
||||
}),
|
||||
)
|
||||
let realName = GLib.get_real_name() || ''
|
||||
let displayName =
|
||||
realName && realName !== 'Unknown' ? realName : GLib.get_user_name()
|
||||
userBox.add_child(
|
||||
new St.Label({
|
||||
text: displayName,
|
||||
style_class: 'win10-footer-label',
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
}),
|
||||
)
|
||||
userBtn.set_child(userBox)
|
||||
footer.add_child(userBtn)
|
||||
|
||||
// Power button (right)
|
||||
let powerBtn = new St.Button({
|
||||
style_class: 'win10-footer-button win10-power-button',
|
||||
can_focus: true,
|
||||
})
|
||||
powerBtn.set_child(
|
||||
new St.Icon({ icon_name: 'system-shutdown-symbolic', icon_size: 20 }),
|
||||
)
|
||||
powerBtn.connect('clicked', () => this._openPowerMenu(powerBtn))
|
||||
this._powerBtn = powerBtn
|
||||
footer.add_child(powerBtn)
|
||||
|
||||
return footer
|
||||
}
|
||||
|
||||
_openPowerMenu(powerBtn) {
|
||||
// Toggle: clicking again while open closes it
|
||||
if (this._powerPopup) {
|
||||
this._powerPopup.destroy()
|
||||
return
|
||||
}
|
||||
|
||||
let popup = new St.BoxLayout({
|
||||
style_class: 'win10-power-popup',
|
||||
vertical: true,
|
||||
reactive: true,
|
||||
})
|
||||
|
||||
for (let { label, icon, fn } of [
|
||||
{
|
||||
label: _('Lock'),
|
||||
icon: 'changes-prevent-symbolic',
|
||||
fn: () => {
|
||||
this.close()
|
||||
Util.spawn(['loginctl', 'lock-session'])
|
||||
},
|
||||
},
|
||||
{
|
||||
label: _('Sign out'),
|
||||
icon: 'system-log-out-symbolic',
|
||||
fn: () => Util.spawn(['gnome-session-quit', '--logout']),
|
||||
},
|
||||
{
|
||||
label: _('Sleep'),
|
||||
icon: 'weather-clear-night-symbolic',
|
||||
fn: () => {
|
||||
this.close()
|
||||
Util.spawn(['systemctl', 'suspend'])
|
||||
},
|
||||
},
|
||||
{
|
||||
label: _('Restart'),
|
||||
icon: 'system-reboot-symbolic',
|
||||
fn: () => Util.spawn(['gnome-session-quit', '--reboot']),
|
||||
},
|
||||
{
|
||||
label: _('Shut down'),
|
||||
icon: 'system-shutdown-symbolic',
|
||||
fn: () => Util.spawn(['gnome-session-quit', '--power-off']),
|
||||
},
|
||||
]) {
|
||||
let btn = new St.Button({
|
||||
style_class: 'win10-power-item',
|
||||
x_align: Clutter.ActorAlign.FILL,
|
||||
x_expand: true,
|
||||
can_focus: true,
|
||||
})
|
||||
let box = new St.BoxLayout()
|
||||
box.add_child(
|
||||
new St.Icon({
|
||||
icon_name: icon,
|
||||
icon_size: 16,
|
||||
style_class: 'win10-power-icon',
|
||||
}),
|
||||
)
|
||||
box.add_child(
|
||||
new St.Label({
|
||||
text: label,
|
||||
style_class: 'win10-power-label',
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
}),
|
||||
)
|
||||
btn.set_child(box)
|
||||
btn.connect('clicked', () => {
|
||||
popup.destroy()
|
||||
fn()
|
||||
})
|
||||
popup.add_child(btn)
|
||||
}
|
||||
|
||||
Main.uiGroup.add_child(popup)
|
||||
this._powerPopup = popup
|
||||
|
||||
// Position above power button after allocation
|
||||
let idleId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
|
||||
idleId = 0
|
||||
if (!popup.get_parent()) return GLib.SOURCE_REMOVE
|
||||
let [bx, by] = powerBtn.get_transformed_position()
|
||||
popup.set_position(
|
||||
Math.round(bx + powerBtn.width - popup.width),
|
||||
Math.round(by - popup.height),
|
||||
)
|
||||
return GLib.SOURCE_REMOVE
|
||||
})
|
||||
|
||||
let grabDisconnected = false
|
||||
let grabId = global.stage.connect('captured-event', (s, e) => {
|
||||
if (e.type() === Clutter.EventType.BUTTON_PRESS) {
|
||||
let src = e.get_source()
|
||||
if (!popup.contains(src) && src !== powerBtn) {
|
||||
grabDisconnected = true
|
||||
global.stage.disconnect(grabId)
|
||||
if (popup.get_parent()) popup.destroy()
|
||||
}
|
||||
}
|
||||
return Clutter.EVENT_PROPAGATE
|
||||
})
|
||||
|
||||
popup.connect('destroy', () => {
|
||||
if (idleId) {
|
||||
GLib.source_remove(idleId)
|
||||
idleId = 0
|
||||
}
|
||||
if (!grabDisconnected) {
|
||||
grabDisconnected = true
|
||||
global.stage.disconnect(grabId)
|
||||
}
|
||||
this._powerPopup = null
|
||||
})
|
||||
}
|
||||
|
||||
_populatePinnedGrid() {
|
||||
this._pinnedGrid.remove_all_children()
|
||||
|
||||
let favorites = AppFavorites.getAppFavorites().getFavorites()
|
||||
let favIds = new Set(favorites.map(a => a.get_id()))
|
||||
let others = Gio.AppInfo.get_all()
|
||||
.filter(info => info.should_show() && !favIds.has(info.get_id()))
|
||||
.map(info => this._appSystem.lookup_app(info.get_id()))
|
||||
.filter(app => app !== null)
|
||||
.sort((a, b) => a.get_name().localeCompare(b.get_name()))
|
||||
let apps = [...favorites, ...others]
|
||||
|
||||
let row = null
|
||||
apps.forEach((app, i) => {
|
||||
if (i % GRID_COLUMNS === 0) {
|
||||
row = new St.BoxLayout({ x_expand: true })
|
||||
this._pinnedGrid.add_child(row)
|
||||
}
|
||||
row.add_child(this._makeGridButton(app))
|
||||
})
|
||||
}
|
||||
|
||||
_populateAllAppsList() {
|
||||
this._allAppsList.remove_all_children()
|
||||
|
||||
let apps = Gio.AppInfo.get_all()
|
||||
.filter(info => info.should_show())
|
||||
.map(info => this._appSystem.lookup_app(info.get_id()))
|
||||
.filter(app => app !== null)
|
||||
.sort((a, b) => a.get_name().localeCompare(b.get_name()))
|
||||
|
||||
let currentLetter = null
|
||||
for (let app of apps) {
|
||||
let firstChar = (app.get_name()[0] || '#').toUpperCase()
|
||||
if (firstChar !== currentLetter) {
|
||||
currentLetter = firstChar
|
||||
this._allAppsList.add_child(
|
||||
new St.Label({ text: firstChar, style_class: 'win10-alpha-sep' }),
|
||||
)
|
||||
}
|
||||
this._allAppsList.add_child(this._makeListButton(app))
|
||||
}
|
||||
}
|
||||
|
||||
_makeGridButton(app) {
|
||||
let btn = new St.Button({
|
||||
style_class: 'win10-grid-app-btn',
|
||||
width: CELL_SIZE,
|
||||
height: CELL_SIZE,
|
||||
reactive: true,
|
||||
track_hover: true,
|
||||
can_focus: true,
|
||||
})
|
||||
let box = new St.BoxLayout({
|
||||
vertical: true,
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
})
|
||||
box.add_child(app.create_icon_texture(APP_ICON_SIZE))
|
||||
let label = new St.Label({
|
||||
text: app.get_name(),
|
||||
style_class: 'win10-grid-app-label',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
})
|
||||
label.clutter_text.ellipsize = Pango.EllipsizeMode.END
|
||||
label.clutter_text.line_wrap = false
|
||||
box.add_child(label)
|
||||
btn.set_child(box)
|
||||
btn.connect('clicked', () => {
|
||||
this.close()
|
||||
app.activate()
|
||||
})
|
||||
return btn
|
||||
}
|
||||
|
||||
_makeListButton(app) {
|
||||
let btn = new St.Button({
|
||||
style_class: 'win10-list-app-btn',
|
||||
x_expand: true,
|
||||
x_align: Clutter.ActorAlign.FILL,
|
||||
reactive: true,
|
||||
track_hover: true,
|
||||
can_focus: true,
|
||||
})
|
||||
let box = new St.BoxLayout({
|
||||
x_expand: true,
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
})
|
||||
let icon = app.create_icon_texture(24)
|
||||
box.add_child(icon)
|
||||
box.add_child(
|
||||
new St.Label({
|
||||
text: app.get_name(),
|
||||
style_class: 'win10-list-app-label',
|
||||
y_align: Clutter.ActorAlign.CENTER,
|
||||
x_expand: true,
|
||||
}),
|
||||
)
|
||||
btn.set_child(box)
|
||||
btn.connect('clicked', () => {
|
||||
this.close()
|
||||
app.activate()
|
||||
})
|
||||
return btn
|
||||
}
|
||||
|
||||
_showView(which) {
|
||||
this._pinnedView.visible = which === 'pinned'
|
||||
this._allAppsView.visible = which === 'all'
|
||||
this._searchView.visible = which === 'search'
|
||||
|
||||
if (which === 'all' && this._allAppsList.get_n_children() === 0) {
|
||||
this._populateAllAppsList()
|
||||
}
|
||||
}
|
||||
|
||||
_onSearchTextChanged() {
|
||||
this._timeoutsHandler.add(['searchDebounce', 150, () => {
|
||||
let query = this._searchEntry.get_text().trim().toLowerCase()
|
||||
if (query.length === 0) this._showView('pinned')
|
||||
else this._doSearch(query)
|
||||
}])
|
||||
}
|
||||
|
||||
_doSearch(query) {
|
||||
this._searchList.remove_all_children()
|
||||
|
||||
let apps = Gio.AppInfo.get_all()
|
||||
.filter(info => {
|
||||
if (!info.should_show()) return false
|
||||
let name = info.get_name().toLowerCase()
|
||||
let desc = (info.get_description() || '').toLowerCase()
|
||||
return name.includes(query) || desc.includes(query)
|
||||
})
|
||||
.map(info => this._appSystem.lookup_app(info.get_id()))
|
||||
.filter(app => app !== null)
|
||||
.sort((a, b) => a.get_name().localeCompare(b.get_name()))
|
||||
|
||||
if (apps.length === 0) {
|
||||
this._searchList.add_child(
|
||||
new St.Label({
|
||||
text: _('No results found'),
|
||||
style_class: 'win10-no-results',
|
||||
x_align: Clutter.ActorAlign.CENTER,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
for (let app of apps) {
|
||||
this._searchList.add_child(this._makeListButton(app))
|
||||
}
|
||||
}
|
||||
|
||||
this._showView('search')
|
||||
}
|
||||
|
||||
open(sourceActor) {
|
||||
if (this.isOpen) return
|
||||
this.isOpen = true
|
||||
this._sourceActor = sourceActor || null
|
||||
this.remove_all_transitions()
|
||||
|
||||
this._searchEntry.set_text('')
|
||||
this._populatePinnedGrid()
|
||||
this._showView('pinned')
|
||||
this._updatePosition(sourceActor)
|
||||
|
||||
this.visible = true
|
||||
this.opacity = 0
|
||||
this.translation_y = 10
|
||||
this.ease({
|
||||
opacity: 255,
|
||||
translation_y: 0,
|
||||
duration: 180,
|
||||
mode: Clutter.AnimationMode.EASE_OUT_QUAD,
|
||||
})
|
||||
|
||||
// Defer captured-event + focus until after the triggering button-press
|
||||
// finishes propagating, so we don't catch our own opening click.
|
||||
GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
|
||||
if (!this.isOpen) return GLib.SOURCE_REMOVE
|
||||
this._captureId = global.stage.connect(
|
||||
'captured-event', this._onCapturedEvent.bind(this),
|
||||
)
|
||||
// Close when an app window gains focus (Wayland: window clicks bypass stage)
|
||||
this._focusId = global.display.connect(
|
||||
'notify::focus-window', () => { if (this.isOpen) this.close() },
|
||||
)
|
||||
this._searchEntry.grab_key_focus()
|
||||
return GLib.SOURCE_REMOVE
|
||||
})
|
||||
}
|
||||
|
||||
close() {
|
||||
if (!this.isOpen) return
|
||||
this.isOpen = false
|
||||
if (this._captureId) {
|
||||
global.stage.disconnect(this._captureId)
|
||||
this._captureId = 0
|
||||
}
|
||||
if (this._focusId) {
|
||||
global.display.disconnect(this._focusId)
|
||||
this._focusId = 0
|
||||
}
|
||||
this._sourceActor = null
|
||||
this.remove_all_transitions()
|
||||
|
||||
if (this._powerPopup) {
|
||||
this._powerPopup.destroy()
|
||||
this._powerPopup = null
|
||||
}
|
||||
|
||||
this.ease({
|
||||
opacity: 0,
|
||||
translation_y: 10,
|
||||
duration: 120,
|
||||
mode: Clutter.AnimationMode.EASE_IN_QUAD,
|
||||
onComplete: () => {
|
||||
if (!this.isOpen) this.visible = false
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
toggle(sourceActor) {
|
||||
if (this.isOpen) this.close()
|
||||
else this.open(sourceActor)
|
||||
}
|
||||
|
||||
_updatePosition(sourceActor) {
|
||||
let monitor = this._dtpPanel.monitor
|
||||
let panelPos = this._dtpPanel.geom.position
|
||||
|
||||
let menuX, menuY
|
||||
|
||||
if (sourceActor) {
|
||||
let [ax, ay] = sourceActor.get_transformed_position()
|
||||
let aw = sourceActor.width
|
||||
let ah = sourceActor.height
|
||||
|
||||
switch (panelPos) {
|
||||
case St.Side.BOTTOM:
|
||||
menuX = ax
|
||||
menuY = ay - MENU_HEIGHT
|
||||
break
|
||||
case St.Side.TOP:
|
||||
menuX = ax
|
||||
menuY = ay + ah
|
||||
break
|
||||
case St.Side.LEFT:
|
||||
menuX = ax + aw
|
||||
menuY = ay
|
||||
break
|
||||
case St.Side.RIGHT:
|
||||
menuX = ax - MENU_WIDTH
|
||||
menuY = ay
|
||||
break
|
||||
default:
|
||||
menuX = ax
|
||||
menuY = ay - MENU_HEIGHT
|
||||
}
|
||||
} else {
|
||||
menuX = monitor.x + 4
|
||||
menuY = monitor.y + monitor.height - MENU_HEIGHT
|
||||
}
|
||||
|
||||
// Clamp to monitor bounds
|
||||
menuX = Math.max(
|
||||
monitor.x + 4,
|
||||
Math.min(menuX, monitor.x + monitor.width - MENU_WIDTH - 4),
|
||||
)
|
||||
menuY = Math.max(
|
||||
monitor.y + 4,
|
||||
Math.min(menuY, monitor.y + monitor.height - MENU_HEIGHT - 4),
|
||||
)
|
||||
|
||||
this.set_size(MENU_WIDTH, MENU_HEIGHT)
|
||||
this._outer.set_size(MENU_WIDTH, MENU_HEIGHT)
|
||||
this.set_position(Math.round(menuX), Math.round(menuY))
|
||||
}
|
||||
|
||||
_onCapturedEvent(_actor, event) {
|
||||
if (!this.isOpen) return Clutter.EVENT_PROPAGATE
|
||||
|
||||
if (event.type() === Clutter.EventType.BUTTON_PRESS) {
|
||||
let [ex, ey] = event.get_coords()
|
||||
let [mx, my] = this.get_transformed_position()
|
||||
if (ex < mx || ex > mx + this.width || ey < my || ey > my + this.height) {
|
||||
// Let the source actor (show-apps button) handle its own toggle
|
||||
let src = event.get_source()
|
||||
if (this._sourceActor && this._sourceActor.contains(src))
|
||||
return Clutter.EVENT_PROPAGATE
|
||||
this.close()
|
||||
}
|
||||
} else if (event.type() === Clutter.EventType.KEY_PRESS) {
|
||||
if (event.get_key_symbol() === Clutter.KEY_Escape) {
|
||||
this.close()
|
||||
return Clutter.EVENT_STOP
|
||||
}
|
||||
}
|
||||
|
||||
return Clutter.EVENT_PROPAGATE
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this._captureId) {
|
||||
global.stage.disconnect(this._captureId)
|
||||
this._captureId = 0
|
||||
}
|
||||
if (this._focusId) {
|
||||
global.display.disconnect(this._focusId)
|
||||
this._focusId = 0
|
||||
}
|
||||
if (this._powerPopup) {
|
||||
this._powerPopup.destroy()
|
||||
this._powerPopup = null
|
||||
}
|
||||
if (this._customStylesheet) {
|
||||
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme()
|
||||
theme.unload_stylesheet(this._customStylesheet)
|
||||
this._customStylesheet = null
|
||||
}
|
||||
this._timeoutsHandler.destroy()
|
||||
this._signalsHandler.destroy()
|
||||
super.destroy()
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -201,3 +201,173 @@
|
||||
#uiGroup.br25 .vesperostaskbarMainPanel .panel-button.clock-display .clock {
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
/* ── Windows 10-style Start Menu ────────────────────────────────────────── */
|
||||
|
||||
.win10-start-menu {
|
||||
background-color: #202020;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
/* Search bar */
|
||||
.win10-search-entry {
|
||||
background-color: #2d2d2d;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 4px;
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
margin: 12px 12px 4px;
|
||||
padding: 8px 12px;
|
||||
caret-color: #0078d4;
|
||||
}
|
||||
|
||||
.win10-search-entry:focus {
|
||||
border-color: #0078d4;
|
||||
}
|
||||
|
||||
/* Section headers */
|
||||
.win10-section-header {
|
||||
padding: 8px 12px 4px;
|
||||
}
|
||||
|
||||
.win10-section-label {
|
||||
color: #b0b0b0;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.win10-link-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #b0b0b0;
|
||||
font-size: 12px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.win10-link-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.win10-back-icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
/* App grid (pinned) */
|
||||
.win10-app-grid {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.win10-grid-app-btn {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 8px 4px;
|
||||
}
|
||||
|
||||
.win10-grid-app-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.win10-grid-app-label {
|
||||
color: #e0e0e0;
|
||||
font-size: 11px;
|
||||
margin-top: 4px;
|
||||
text-align: center;
|
||||
/* max-width matches CELL_SIZE so long names are clipped */
|
||||
max-width: 107px;
|
||||
}
|
||||
|
||||
/* App list (all apps / search results) */
|
||||
.win10-list-app-btn {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.win10-list-app-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.win10-list-app-label {
|
||||
color: #e0e0e0;
|
||||
font-size: 13px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.win10-alpha-sep {
|
||||
color: #0078d4;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
padding: 8px 12px 2px;
|
||||
}
|
||||
|
||||
.win10-no-results {
|
||||
color: #888888;
|
||||
font-size: 13px;
|
||||
padding: 24px 12px;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.win10-footer {
|
||||
background-color: #141414;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.07);
|
||||
border-radius: 0 0 8px 8px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.win10-footer-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #c0c0c0;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.win10-footer-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.win10-footer-icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.win10-footer-label {
|
||||
color: #c0c0c0;
|
||||
font-size: 13px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
/* Power popup */
|
||||
.win10-power-popup {
|
||||
background-color: #1e1e1e;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 6px;
|
||||
padding: 4px;
|
||||
min-width: 168px;
|
||||
}
|
||||
|
||||
.win10-power-item {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #e0e0e0;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.win10-power-item:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.win10-power-icon {
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.win10-power-label {
|
||||
font-size: 13px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import * as AppIcons from './appIcons.js'
|
||||
import * as PanelManager from './panelManager.js'
|
||||
import * as PanelSettings from './panelSettings.js'
|
||||
import * as Pos from './panelPositions.js'
|
||||
import * as StartMenu from './startMenu.js'
|
||||
import * as Utils from './utils.js'
|
||||
import * as WindowPreview from './windowPreview.js'
|
||||
import { SETTINGS, tracker } from './extension.js'
|
||||
@@ -248,6 +249,9 @@ export const Taskbar = class extends EventEmitter {
|
||||
this._showAppsIcon.icon.setIconSize(this.iconSize)
|
||||
this._hookUpLabel(this._showAppsIcon, this._showAppsIconWrapper)
|
||||
|
||||
this._startMenu = new StartMenu.Win10StartMenu(panel)
|
||||
this._ignoreShowAppsToggle = false
|
||||
|
||||
this._container.add_child(new St.Widget({ width: 0, reactive: false }))
|
||||
this._container.add_child(this._scrollView)
|
||||
|
||||
@@ -396,6 +400,11 @@ export const Taskbar = class extends EventEmitter {
|
||||
this._waitIdleId = 0
|
||||
}
|
||||
|
||||
if (this._startMenu) {
|
||||
this._startMenu.destroy()
|
||||
this._startMenu = null
|
||||
}
|
||||
|
||||
this._timeoutsHandler.destroy()
|
||||
this.iconAnimator.destroy()
|
||||
|
||||
@@ -1300,55 +1309,26 @@ export const Taskbar = class extends EventEmitter {
|
||||
}
|
||||
|
||||
_onShowAppsButtonToggled() {
|
||||
// Sync the status of the default appButtons. Only if the two statuses are
|
||||
// different, that means the user interacted with the extension provided
|
||||
// application button, cutomize the behaviour. Otherwise the shell has changed the
|
||||
// status (due to the _syncShowAppsButtonToggled function below) and it
|
||||
// has already performed the desired action.
|
||||
// Guard against re-entry when we programmatically uncheck the button.
|
||||
if (this._ignoreShowAppsToggle) return
|
||||
|
||||
// Only act when the user interacted with our button (statuses differ).
|
||||
// When the shell itself changes the status _syncShowAppsButtonToggled
|
||||
// keeps them in sync, so we don't need to do anything here.
|
||||
let selector = SearchController
|
||||
|
||||
if (
|
||||
selector._showAppsButton &&
|
||||
selector._showAppsButton.checked !== this.showAppsButton.checked
|
||||
) {
|
||||
// find visible view
|
||||
|
||||
if (this.showAppsButton.checked) {
|
||||
//override escape key to return to the desktop when entering the overview using the showapps button
|
||||
SearchController._onStageKeyPress = function (actor, event) {
|
||||
if (
|
||||
Main.modalCount == 1 &&
|
||||
event.get_key_symbol() === Clutter.KEY_Escape
|
||||
) {
|
||||
this._searchActive ? this.reset() : Main.overview.hide()
|
||||
// Uncheck the toggle immediately – the start menu manages its own
|
||||
// open/closed state independently of the button's checked property.
|
||||
this._ignoreShowAppsToggle = true
|
||||
this.showAppsButton.checked = false
|
||||
this._ignoreShowAppsToggle = false
|
||||
|
||||
return Clutter.EVENT_STOP
|
||||
}
|
||||
|
||||
return Object.getPrototypeOf(this)._onStageKeyPress.call(
|
||||
this,
|
||||
actor,
|
||||
event,
|
||||
)
|
||||
}
|
||||
|
||||
let overviewHiddenId = Main.overview.connect('hidden', () => {
|
||||
Main.overview.disconnect(overviewHiddenId)
|
||||
delete SearchController._onStageKeyPress
|
||||
})
|
||||
|
||||
// force exiting overview if needed
|
||||
if (!Main.overview._shown) {
|
||||
this.forcedOverview = true
|
||||
}
|
||||
|
||||
//temporarily use as primary the monitor on which the showapps btn was clicked, this is
|
||||
//restored by the panel when exiting the overview
|
||||
this.dtpPanel.panelManager.setFocusedMonitor(this.dtpPanel.monitor)
|
||||
|
||||
// Finally show the overview
|
||||
selector._showAppsButton.checked = true
|
||||
Main.overview.show(2 /*APP_GRID*/)
|
||||
this._startMenu.toggle(this.showAppsButton)
|
||||
} else {
|
||||
if (this.forcedOverview) {
|
||||
// force exiting overview if needed
|
||||
|
||||
162
src/themes/win10-dark.css
Normal file
162
src/themes/win10-dark.css
Normal file
@@ -0,0 +1,162 @@
|
||||
/* Windows 10 — Dark */
|
||||
|
||||
.win10-start-menu {
|
||||
background-color: #202020;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.win10-search-entry {
|
||||
background-color: #2d2d2d;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 4px;
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
margin: 12px 12px 4px;
|
||||
padding: 8px 12px;
|
||||
caret-color: #0078d4;
|
||||
}
|
||||
|
||||
.win10-search-entry:focus {
|
||||
border-color: #0078d4;
|
||||
}
|
||||
|
||||
.win10-section-header {
|
||||
padding: 8px 12px 4px;
|
||||
}
|
||||
|
||||
.win10-section-label {
|
||||
color: #b0b0b0;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.win10-link-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #b0b0b0;
|
||||
font-size: 12px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.win10-link-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.win10-back-icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.win10-app-grid {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.win10-grid-app-btn {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 4px;
|
||||
}
|
||||
|
||||
.win10-grid-app-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.win10-grid-app-label {
|
||||
color: #e0e0e0;
|
||||
font-size: 11px;
|
||||
margin-top: 4px;
|
||||
text-align: center;
|
||||
max-width: 107px;
|
||||
}
|
||||
|
||||
.win10-list-app-btn {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.win10-list-app-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.win10-list-app-label {
|
||||
color: #e0e0e0;
|
||||
font-size: 13px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.win10-alpha-sep {
|
||||
color: #0078d4;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
padding: 8px 12px 2px;
|
||||
}
|
||||
|
||||
.win10-no-results {
|
||||
color: #888888;
|
||||
font-size: 13px;
|
||||
padding: 24px 12px;
|
||||
}
|
||||
|
||||
.win10-footer {
|
||||
background-color: #141414;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.07);
|
||||
border-radius: 0 0 4px 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.win10-footer-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #c0c0c0;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.win10-footer-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.win10-footer-icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.win10-footer-label {
|
||||
color: #c0c0c0;
|
||||
font-size: 13px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.win10-power-popup {
|
||||
background-color: #1e1e1e;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
min-width: 168px;
|
||||
}
|
||||
|
||||
.win10-power-item {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #e0e0e0;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.win10-power-item:hover {
|
||||
background-color: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.win10-power-icon {
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.win10-power-label {
|
||||
font-size: 13px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
162
src/themes/win10-light.css
Normal file
162
src/themes/win10-light.css
Normal file
@@ -0,0 +1,162 @@
|
||||
/* Windows 10 — Light */
|
||||
|
||||
.win10-start-menu {
|
||||
background-color: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.win10-search-entry {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.18);
|
||||
border-radius: 4px;
|
||||
color: #1a1a1a;
|
||||
font-size: 13px;
|
||||
margin: 12px 12px 4px;
|
||||
padding: 8px 12px;
|
||||
caret-color: #0078d4;
|
||||
}
|
||||
|
||||
.win10-search-entry:focus {
|
||||
border-color: #0078d4;
|
||||
}
|
||||
|
||||
.win10-section-header {
|
||||
padding: 8px 12px 4px;
|
||||
}
|
||||
|
||||
.win10-section-label {
|
||||
color: #444444;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.win10-link-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #444444;
|
||||
font-size: 12px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.win10-link-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.07);
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.win10-back-icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.win10-app-grid {
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.win10-grid-app-btn {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 4px;
|
||||
}
|
||||
|
||||
.win10-grid-app-btn:hover {
|
||||
background-color: rgba(0, 0, 0, 0.07);
|
||||
}
|
||||
|
||||
.win10-grid-app-label {
|
||||
color: #1a1a1a;
|
||||
font-size: 11px;
|
||||
margin-top: 4px;
|
||||
text-align: center;
|
||||
max-width: 107px;
|
||||
}
|
||||
|
||||
.win10-list-app-btn {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.win10-list-app-btn:hover {
|
||||
background-color: rgba(0, 0, 0, 0.07);
|
||||
}
|
||||
|
||||
.win10-list-app-label {
|
||||
color: #1a1a1a;
|
||||
font-size: 13px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.win10-alpha-sep {
|
||||
color: #0078d4;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
padding: 8px 12px 2px;
|
||||
}
|
||||
|
||||
.win10-no-results {
|
||||
color: #666666;
|
||||
font-size: 13px;
|
||||
padding: 24px 12px;
|
||||
}
|
||||
|
||||
.win10-footer {
|
||||
background-color: #dcdcdc;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.08);
|
||||
border-radius: 0 0 4px 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.win10-footer-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #2a2a2a;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.win10-footer-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.08);
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.win10-footer-icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.win10-footer-label {
|
||||
color: #2a2a2a;
|
||||
font-size: 13px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.win10-power-popup {
|
||||
background-color: #e8e8e8;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
min-width: 168px;
|
||||
}
|
||||
|
||||
.win10-power-item {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #1a1a1a;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.win10-power-item:hover {
|
||||
background-color: rgba(0, 0, 0, 0.07);
|
||||
}
|
||||
|
||||
.win10-power-icon {
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.win10-power-label {
|
||||
font-size: 13px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
165
src/themes/win11-dark.css
Normal file
165
src/themes/win11-dark.css
Normal file
@@ -0,0 +1,165 @@
|
||||
/* Windows 11 — Dark
|
||||
* Approximates the Win11 Mica/acrylic aesthetic: rounded corners,
|
||||
* layered semi-transparent surfaces, and the lighter blue accent. */
|
||||
|
||||
.win10-start-menu {
|
||||
background-color: #272727;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.win10-search-entry {
|
||||
background-color: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 20px;
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
margin: 14px 14px 6px;
|
||||
padding: 9px 16px;
|
||||
caret-color: #60cdff;
|
||||
}
|
||||
|
||||
.win10-search-entry:focus {
|
||||
border-color: #60cdff;
|
||||
background-color: rgba(255, 255, 255, 0.09);
|
||||
}
|
||||
|
||||
.win10-section-header {
|
||||
padding: 10px 14px 4px;
|
||||
}
|
||||
|
||||
.win10-section-label {
|
||||
color: #a0a0a0;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.win10-link-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: #a0a0a0;
|
||||
font-size: 12px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.win10-link-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.07);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.win10-back-icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.win10-app-grid {
|
||||
padding: 4px 2px;
|
||||
}
|
||||
|
||||
.win10-grid-app-btn {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 10px 4px;
|
||||
}
|
||||
|
||||
.win10-grid-app-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.07);
|
||||
}
|
||||
|
||||
.win10-grid-app-label {
|
||||
color: #e8e8e8;
|
||||
font-size: 11px;
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
max-width: 107px;
|
||||
}
|
||||
|
||||
.win10-list-app-btn {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 7px 14px;
|
||||
}
|
||||
|
||||
.win10-list-app-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.07);
|
||||
}
|
||||
|
||||
.win10-list-app-label {
|
||||
color: #e8e8e8;
|
||||
font-size: 13px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.win10-alpha-sep {
|
||||
color: #60cdff;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
padding: 10px 14px 2px;
|
||||
}
|
||||
|
||||
.win10-no-results {
|
||||
color: #777777;
|
||||
font-size: 13px;
|
||||
padding: 28px 14px;
|
||||
}
|
||||
|
||||
.win10-footer {
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-radius: 0 0 12px 12px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.win10-footer-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: #c8c8c8;
|
||||
padding: 9px 14px;
|
||||
}
|
||||
|
||||
.win10-footer-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.09);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.win10-footer-icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.win10-footer-label {
|
||||
color: #c8c8c8;
|
||||
font-size: 13px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.win10-power-popup {
|
||||
background-color: #2f2f2f;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
padding: 6px;
|
||||
min-width: 176px;
|
||||
}
|
||||
|
||||
.win10-power-item {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: #e8e8e8;
|
||||
padding: 9px 14px;
|
||||
}
|
||||
|
||||
.win10-power-item:hover {
|
||||
background-color: rgba(255, 255, 255, 0.07);
|
||||
}
|
||||
|
||||
.win10-power-icon {
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.win10-power-label {
|
||||
font-size: 13px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
165
src/themes/win11-light.css
Normal file
165
src/themes/win11-light.css
Normal file
@@ -0,0 +1,165 @@
|
||||
/* Windows 11 — Light
|
||||
* Approximates the Win11 Mica/acrylic aesthetic: rounded corners,
|
||||
* layered semi-transparent surfaces, and the standard blue accent. */
|
||||
|
||||
.win10-start-menu {
|
||||
background-color: #f3f3f3;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.win10-search-entry {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.14);
|
||||
border-radius: 20px;
|
||||
color: #1a1a1a;
|
||||
font-size: 13px;
|
||||
margin: 14px 14px 6px;
|
||||
padding: 9px 16px;
|
||||
caret-color: #0067c0;
|
||||
}
|
||||
|
||||
.win10-search-entry:focus {
|
||||
border-color: #0067c0;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.win10-section-header {
|
||||
padding: 10px 14px 4px;
|
||||
}
|
||||
|
||||
.win10-section-label {
|
||||
color: #555555;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.win10-link-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: #555555;
|
||||
font-size: 12px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.win10-link-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.06);
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.win10-back-icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.win10-app-grid {
|
||||
padding: 4px 2px;
|
||||
}
|
||||
|
||||
.win10-grid-app-btn {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
padding: 10px 4px;
|
||||
}
|
||||
|
||||
.win10-grid-app-btn:hover {
|
||||
background-color: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.win10-grid-app-label {
|
||||
color: #1a1a1a;
|
||||
font-size: 11px;
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
max-width: 107px;
|
||||
}
|
||||
|
||||
.win10-list-app-btn {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 7px 14px;
|
||||
}
|
||||
|
||||
.win10-list-app-btn:hover {
|
||||
background-color: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.win10-list-app-label {
|
||||
color: #1a1a1a;
|
||||
font-size: 13px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.win10-alpha-sep {
|
||||
color: #0067c0;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
padding: 10px 14px 2px;
|
||||
}
|
||||
|
||||
.win10-no-results {
|
||||
color: #888888;
|
||||
font-size: 13px;
|
||||
padding: 28px 14px;
|
||||
}
|
||||
|
||||
.win10-footer {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.07);
|
||||
border-radius: 0 0 12px 12px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.win10-footer-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: #2a2a2a;
|
||||
padding: 9px 14px;
|
||||
}
|
||||
|
||||
.win10-footer-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.07);
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.win10-footer-icon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.win10-footer-label {
|
||||
color: #2a2a2a;
|
||||
font-size: 13px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
.win10-power-popup {
|
||||
background-color: #ebebeb;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 10px;
|
||||
padding: 6px;
|
||||
min-width: 176px;
|
||||
}
|
||||
|
||||
.win10-power-item {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: #1a1a1a;
|
||||
padding: 9px 14px;
|
||||
}
|
||||
|
||||
.win10-power-item:hover {
|
||||
background-color: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.win10-power-icon {
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.win10-power-label {
|
||||
font-size: 13px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
Reference in New Issue
Block a user