Maps fit beside the media island

The map menus' width follows the monitor: from its right edge to the
island's compact pill, less a 16 px gap (the pill's pop is 15 px a side);
822 px on 1920. AREA_H 480, no alternating rings (simulated: no label
overlaps up to 8 nodes at this size). The password popup centres on it.
This commit is contained in:
server2
2026-10-13 06:09:06 +02:00
parent a7c49a16c0
commit 35386e84f1
5 changed files with 31 additions and 28 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ The panel has:
- *Hotspot* (iwd access point): see [Hotspot setup](#hotspot-setup).
- *Ethernet*: every real wired card in `/sys/class/net` (a device behind it, not wireless): connected or no cable, and the link speed it negotiated (`speed`, as 1 Gb/s). A click turns the panel into its details (it morphs like into any menu; the back button returns): the interface and driver, status (speed, duplex), every IPv4 and IPv6 address, gateway, DNS servers, MAC, MTU and traffic since boot (`ip -o addr`, `ip -o route`, `/etc/resolv.conf`, sysfs). Addresses, gateway, DNS and MAC have a Copy chip; a click anywhere on such a row copies it (addresses without their prefix).
- **Popups:** questions (a password, PIN or passkey), a code to confirm, a saved password or why something failed, in a small card over the menu. Only a popup with a field takes the keyboard (Enter answers, Escape cancels); the others leave keys to other windows until clicked.
- **Menus:** one shape the whole way: the panel fades its content out and takes the menu's size (the Wi-Fi and Bluetooth maps are 920 × 460 px, with at most 8 networks or devices, the strongest first, every other one a little further in so neighbouring labels don't meet), then the menu's content fades in over it from a window of its own (so the maps' animation redraws only that window). The back button returns to the panel. Moving the pointer off both closes the menu where it is: its content fades out and it shrinks straight into the pill, without the panel growing back first.
- **Menus:** one shape the whole way: the panel fades its content out and takes the menu's size (the Wi-Fi and Bluetooth maps reach from the monitor's right edge to the media island's pill, leaving a 16 px gap, room for the pill's pop: 822 × 480 px on a 1920 px monitor; at most 8 networks or devices, the strongest first, so their labels never meet), then the menu's content fades in over it from a window of its own (so the maps' animation redraws only that window). The back button returns to the panel. Moving the pointer off both closes the menu where it is: its content fades out and it shrinks straight into the pill, without the panel growing back first.
- **Sound:** the default output's volume (drag or scroll the slider); the value next to the title is the mute button. With `extra_output` in `shell.conf` a second slider follows for the output whose name contains it (here `NR515`: the `[TX-NR515]` receiver on HDMI 1). Clicking the block elsewhere opens the Sound menu: every output with its own slider and mute; click a name to make it the default output.
- **Display:** the laptop screen's brightness and the keyboard light (`tpacpi::kbd_backlight`: Off, Low, High), written straight to their sysfs files (a udev rule makes them writable by everyone on this laptop), else through logind, which refuses a shell without a seat (one started over ssh). For external monitors see [External monitor brightness](#external-monitor-brightness).
- **Performance:** the firmware profiles in `/sys/firmware/acpi/platform_profile_choices` (Low power, Balanced, Performance), and the battery's state and time left. Picking one sets the platform profile and the CPU governor and energy preference with it: Low power = `powersave` + `power`, Balanced = `powersave` + `balance_performance`, Performance = the `performance` governor.
+25 -15
View File
@@ -18,7 +18,7 @@ use amane::{
use crate::edge::{self, Edge, HIDE_H};
use crate::glyph::{Glyph, icon};
use crate::island::{self, CHIP, CHIP_HOVER, CHIP_OPEN, CLEAR, COMPACT_H, HOVER_MS, PILL_GONE, SHADOW, SHAPE_DONE, TOP_GAP, TRACK, backdrop, faded, mix, phases, veiled};
use crate::map::{self, AREA_H, Centre, Link, MAP_W, NODES, Node};
use crate::map::{self, AREA_H, Centre, Link, NODES, Node};
use crate::parse;
use crate::prompt;
use crate::sound::Sound;
@@ -47,8 +47,16 @@ const OUTPUTS: usize = 8;
// the pill and the panel sit this far from the monitor's right edge
pub const RIGHT: f32 = 12.0;
// both windows are as wide as a map menu, the widest the panel becomes
const WINDOW_W: f32 = RIGHT + MAP_W + SHADOW;
// between a map menu and the media island's pill: room for the pill's pop (15 px a side) and a little air
const ISLAND_GAP: f32 = 16.0;
// the narrowest a map gets, on a small monitor (where it then runs under the island)
const MIN_MAP_W: f32 = 600.0;
// how wide the Wi-Fi and Bluetooth maps are on a monitor: from its right edge to the island's pill, less the gap
pub fn map_width(monitor_w: f32) -> f32 {
(monitor_w / 2.0 - island::COMPACT_W / 2.0 - ISLAND_GAP - RIGHT).max(MIN_MAP_W)
}
// the pill is as wide as its icons, PILL_PAD on each side, PILL_GAP apart
const PILL_PAD: f32 = 16.0;
@@ -371,7 +379,9 @@ pub fn view(monitor: &Monitor) -> LayerWindow {
let slide = edge::hidden_by(Edge::read().shown(here), open, HIDE_H);
// as wide as the widest thing it can become, a map menu; the shape grows leftwards from the corner
let window_w = WINDOW_W;
// both windows are as wide as a map menu, the widest the panel becomes
let map_w = map_width(monitor.width as f32);
let window_w = RIGHT + map_w + SHADOW;
let window = LayerWindow::new()
.width(window_w)
@@ -431,7 +441,7 @@ pub fn view(monitor: &Monitor) -> LayerWindow {
let menu_size = match (control.menu.filter(|_| menu_t > 0.0), &sound) {
(Some(menu), Some(sound)) => {
let content = menu_content(menu, &control, &links, &bluetooth, sound);
let content = menu_content(menu, map_w, &control, &links, &bluetooth, sound);
Some((content.width, menu_height(&content)))
}
@@ -1608,7 +1618,7 @@ pub fn menu_view(monitor: &Monitor) -> LayerWindow {
let bluetooth = Bluetooth::read();
let sound = Sound::read();
let content = menu_content(menu, &control, &links, &bluetooth, &sound);
let content = menu_content(menu, map_width(monitor.width as f32), &control, &links, &bluetooth, &sound);
let menu_w = content.width;
let menu_h = menu_height(&content);
@@ -1677,10 +1687,10 @@ fn menu_back() {
* a menu's content. both windows call it, the panel for the size it takes; every
* Service is passed in, since a view reading one twice can deadlock against a writer
*/
fn menu_content(menu: Menu, control: &Control, links: &Links, bluetooth: &Bluetooth, sound: &Sound) -> MenuContent {
fn menu_content(menu: Menu, map_w: f32, control: &Control, links: &Links, bluetooth: &Bluetooth, sound: &Sound) -> MenuContent {
match menu {
Menu::Wifi => wifi_menu(control, links),
Menu::Bluetooth => bluetooth_menu(control, bluetooth, links),
Menu::Wifi => wifi_menu(control, links, map_w),
Menu::Bluetooth => bluetooth_menu(control, bluetooth, links, map_w),
Menu::Vpn => vpn_menu(control, links),
Menu::Sound => sound_menu(control, sound),
Menu::Ethernet => ethernet_menu(control),
@@ -1843,7 +1853,7 @@ fn footer(control: &Control, text: &str) -> Rectangle {
.child(Text::new(text).size(11.5).color(Color::blend(theme::now().dim, theme::now().text, lit)))
}
fn wifi_menu(control: &Control, links: &Links) -> MenuContent {
fn wifi_menu(control: &Control, links: &Links, map_w: f32) -> MenuContent {
let wifi = &links.wifi;
// the tile shows "One moment" until the switch has done its work
@@ -1950,16 +1960,16 @@ fn wifi_menu(control: &Control, links: &Links) -> MenuContent {
on_click: Box::new(|| search_again(Menu::Wifi)),
};
body.add(map::map(MAP_W - 2.0 * MENU_PAD, AREA_H, centre, nodes), AREA_H);
body.add(map::map(map_w - 2.0 * MENU_PAD, AREA_H, centre, nodes), AREA_H);
if wifi.service {
body.add(footer(control, "Stop the Wi-Fi service").on_click(|_| later(system::stop_wifi_service, None)), 30.0);
}
body.into_content("Wi-Fi", right, MAP_W)
body.into_content("Wi-Fi", right, map_w)
}
fn bluetooth_menu(control: &Control, bluetooth: &Bluetooth, links: &Links) -> MenuContent {
fn bluetooth_menu(control: &Control, bluetooth: &Bluetooth, links: &Links, map_w: f32) -> MenuContent {
let before = bluetooth_state(bluetooth, links).subtitle;
let right = (bluetooth.available() || links.bluetooth_service).then(|| {
@@ -2059,9 +2069,9 @@ fn bluetooth_menu(control: &Control, bluetooth: &Bluetooth, links: &Links) -> Me
on_click: Box::new(|| search_again(Menu::Bluetooth)),
};
body.add(map::map(MAP_W - 2.0 * MENU_PAD, AREA_H, centre, nodes), AREA_H);
body.add(map::map(map_w - 2.0 * MENU_PAD, AREA_H, centre, nodes), AREA_H);
body.into_content("Bluetooth", right, MAP_W)
body.into_content("Bluetooth", right, map_w)
}
fn vpn_menu(control: &Control, links: &Links) -> MenuContent {
+1 -1
View File
@@ -35,7 +35,7 @@ const PILL_HOVER_GROW: f32 = 10.0;
const BOLD: f32 = 2.0;
// sizes, in pixels
const COMPACT_W: f32 = 220.0;
pub const COMPACT_W: f32 = 220.0;
pub const COMPACT_H: f32 = 38.0;
const WIDE_W: f32 = 560.0;
const WIDE_RADIUS: f32 = 28.0;
+3 -9
View File
@@ -15,11 +15,8 @@ use crate::glyph::{Glyph, glyph_shapes};
use crate::island::{CHIP_OPEN, faded};
use crate::theme;
// how wide the map menus are, against the panel's 360
pub const MAP_W: f32 = 920.0;
// the map's own height, under the menu's header
pub const AREA_H: f32 = 460.0;
pub const AREA_H: f32 = 480.0;
const BUBBLE: f32 = 40.0;
const NODE_R: f32 = 22.0;
@@ -27,9 +24,6 @@ const ICON: f32 = 18.0;
const LABEL_W: f32 = 140.0;
const LABEL_H: f32 = 30.0;
// every other one sits this much further in
const STAGGER: f32 = 0.9;
// how far each one floats from its place, either way
const DRIFT_X: f32 = 26.0;
const DRIFT_Y: f32 = 16.0;
@@ -117,8 +111,8 @@ pub fn map(width: f32, height: f32, centre: Centre, nodes: Vec<Node>) -> Rectang
// evenly round, a quarter step past the top, so no one sits right on top of or under the bubble
let angle = -PI / 2.0 + TAU * (phase + 0.25) / count;
// the stronger the nearer, but never far in; every other one a step further in, so neighbours' labels don't meet
let reach = (1.0 - 0.1 * node.nearness.clamp(0.0, 1.0)) * if index % 2 == 1 { STAGGER } else { 1.0 };
// the stronger the nearer, but never far in, so neighbours' labels don't meet
let reach = 1.0 - 0.1 * node.nearness.clamp(0.0, 1.0);
// pushed out to the inner ellipse if the nearness took it inside
let (ex, ey) = (angle.cos() * rx * reach, angle.sin() * ry * reach);
+1 -2
View File
@@ -21,7 +21,6 @@ use amane::{
use crate::control::{self, Control, RIGHT};
use crate::glyph::{Glyph, icon};
use crate::island::{self, CHIP, CHIP_HOVER, CHIP_OPEN, HOVER_MS, SHADOW, TOP_GAP, backdrop, veiled};
use crate::map::MAP_W;
use crate::parse::{self, Pairing};
use crate::system;
use crate::theme;
@@ -636,7 +635,7 @@ pub fn view(monitor: &Monitor) -> LayerWindow {
.child(veiled(content, POPUP_W, card_h, 22.0, appear, &colors));
// over the middle of the map menu, a little below the control centre's top
let (right, top) = (RIGHT + (MAP_W - POPUP_W) / 2.0 - SHADOW, TOP_GAP + 110.0 - SHADOW);
let (right, top) = (RIGHT + (control::map_width(monitor.width as f32) - POPUP_W) / 2.0 - SHADOW, TOP_GAP + 110.0 - SHADOW);
/*
* the keyboard only while there's something to type: the other popups come on their