Restore the battery menu sections and center their icons
A let-chain collapse in the status menu left the battery blocks inside the notifications arm, so the percentage, level meter, power mode row, and power actions only rendered when notifications were present. Close the notifications block before the battery checks so each menu kind owns its own sections again. Use center_x/center_y on the fixed-size icon containers rather than align_x/align_y, which left badges and the top bar slot sitting off center in libcosmic, and constrain the menu icon to a square 22px slot so the battery glyph is not letterboxed by its own width. Give the power tiles a Fill-width centering container inside their FillPortion(1) buttons so the label and icon stay centered on hover. Drop the two column-alignment TODO wrappers the collapse introduced.
This commit is contained in:
1 parent
e7ee2f14db
commit
c6b548349d
1 file changed
+149
-147
+149
-147
@@ -348,11 +348,12 @@ impl FereseShell {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
// A fixed button height does not center its child in libcosmic.
|
// A fixed button height does not center its child in libcosmic.
|
||||||
// Keep the visual content centered inside the entire click target.
|
// center_y/center_x wrap the content in a centering layout, so use
|
||||||
|
// those rather than align_y/align_x on a fixed-size container.
|
||||||
let content = container(content)
|
let content = container(content)
|
||||||
.align_x(alignment::Horizontal::Center)
|
.align_x(alignment::Horizontal::Center)
|
||||||
.height(metrics.control_height)
|
.height(metrics.control_height)
|
||||||
.align_y(alignment::Vertical::Center);
|
.center_y(metrics.control_height);
|
||||||
let recording_busy = kind == Menu::Recording && self.recorder.busy();
|
let recording_busy = kind == Menu::Recording && self.recorder.busy();
|
||||||
let control = button::custom(content)
|
let control = button::custom(content)
|
||||||
.name(if recording_busy {
|
.name(if recording_busy {
|
||||||
@@ -419,16 +420,19 @@ impl FereseShell {
|
|||||||
let kind = menu.kind;
|
let kind = menu.kind;
|
||||||
let primary = color_with_opacity(theme.text_primary, p);
|
let primary = color_with_opacity(theme.text_primary, p);
|
||||||
let muted = color_with_opacity(theme.text_muted, p);
|
let muted = color_with_opacity(theme.text_muted, p);
|
||||||
let badge = container(accented_icon(
|
let badge = container(
|
||||||
status_icon(kind, &self.status).0,
|
accented_icon(
|
||||||
22,
|
status_icon(kind, &self.status).0,
|
||||||
primary,
|
22,
|
||||||
color_with_opacity(theme.accent, p),
|
primary,
|
||||||
))
|
color_with_opacity(theme.accent, p),
|
||||||
|
)
|
||||||
|
.width(Length::Fixed(22.0)),
|
||||||
|
)
|
||||||
.width(36)
|
.width(36)
|
||||||
.height(36)
|
.height(36)
|
||||||
.align_x(alignment::Horizontal::Center)
|
.center_x(36)
|
||||||
.align_y(alignment::Vertical::Center)
|
.center_y(36)
|
||||||
.class(theme::Container::custom(move |_| container::Style {
|
.class(theme::Container::custom(move |_| container::Style {
|
||||||
background: Some(Background::Color(color_with_opacity(
|
background: Some(Background::Color(color_with_opacity(
|
||||||
theme.accent,
|
theme.accent,
|
||||||
@@ -725,139 +729,137 @@ impl FereseShell {
|
|||||||
p,
|
p,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if menu.kind == Menu::Battery
|
if menu.kind == Menu::Battery
|
||||||
&& let Some(b) = &self.status.battery
|
&& let Some(b) = &self.status.battery
|
||||||
|
{
|
||||||
|
let battery_color = if b.percent < self.config.status.low_battery_threshold
|
||||||
|
&& b.status != "Charging"
|
||||||
{
|
{
|
||||||
let battery_color = if b.percent < self.config.status.low_battery_threshold
|
Color {
|
||||||
&& b.status != "Charging"
|
a: p,
|
||||||
{
|
..Color::from_rgb8(230, 172, 90)
|
||||||
Color {
|
}
|
||||||
a: p,
|
} else {
|
||||||
..Color::from_rgb8(230, 172, 90)
|
primary
|
||||||
}
|
};
|
||||||
} else {
|
rows = rows.push(
|
||||||
primary
|
container(
|
||||||
};
|
row![
|
||||||
rows = rows.push(
|
text(format!("{}%", b.percent)).size(24).width(Length::Fill),
|
||||||
container(
|
accented_icon(
|
||||||
row![
|
status_icon(Menu::Battery, &self.status).0,
|
||||||
text(format!("{}%", b.percent)).size(24).width(Length::Fill),
|
24,
|
||||||
accented_icon(
|
battery_color,
|
||||||
status_icon(Menu::Battery, &self.status).0,
|
color_with_opacity(theme.accent, p),
|
||||||
24,
|
|
||||||
battery_color,
|
|
||||||
color_with_opacity(theme.accent, p),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
.align_y(Alignment::Center),
|
|
||||||
)
|
|
||||||
.padding([2, 0])
|
|
||||||
.class(theme::Container::custom(move |_| container::Style {
|
|
||||||
text_color: Some(battery_color),
|
|
||||||
icon_color: Some(battery_color),
|
|
||||||
..Default::default()
|
|
||||||
})),
|
|
||||||
);
|
|
||||||
rows = rows.push(level_meter(b.percent, battery_color, p));
|
|
||||||
rows = rows.push(
|
|
||||||
text(match b.status.as_str() {
|
|
||||||
"Charging" => "Connected to power",
|
|
||||||
"Discharging" => "Running on battery power",
|
|
||||||
"Full" => "Battery fully charged",
|
|
||||||
_ => "Battery status reported by the system",
|
|
||||||
})
|
|
||||||
.size(12)
|
|
||||||
.class(theme::Text::Color(muted)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if menu.kind == Menu::Battery {
|
|
||||||
rows = rows.push(text("Power mode").size(13).class(theme::Text::Color(muted)));
|
|
||||||
if let Some(profiles) = &self.status.power_profiles {
|
|
||||||
let mut modes = row::with_capacity(3).spacing(6).width(Length::Fill);
|
|
||||||
for (index, (label, icon, profile)) in [
|
|
||||||
(
|
|
||||||
"Power saver",
|
|
||||||
include_bytes!("../assets/icons/status/power-saver.svg").as_slice(),
|
|
||||||
"power-saver",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"Balanced",
|
|
||||||
include_bytes!("../assets/icons/status/power-balanced.svg")
|
|
||||||
.as_slice(),
|
|
||||||
"balanced",
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"Performance",
|
|
||||||
include_bytes!("../assets/icons/status/power-performance.svg")
|
|
||||||
.as_slice(),
|
|
||||||
"performance",
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.align_y(Alignment::Center),
|
||||||
.enumerate()
|
)
|
||||||
{
|
.padding([2, 0])
|
||||||
if profiles.available[index] {
|
.class(theme::Container::custom(move |_| container::Style {
|
||||||
modes = modes.push(power_tile(
|
text_color: Some(battery_color),
|
||||||
label,
|
icon_color: Some(battery_color),
|
||||||
icon,
|
..Default::default()
|
||||||
profile,
|
})),
|
||||||
profiles.active == profile,
|
);
|
||||||
theme,
|
rows = rows.push(level_meter(b.percent, battery_color, p));
|
||||||
p,
|
rows = rows.push(
|
||||||
));
|
text(match b.status.as_str() {
|
||||||
}
|
"Charging" => "Connected to power",
|
||||||
}
|
"Discharging" => "Running on battery power",
|
||||||
// TODO: confirm column align fix
|
"Full" => "Battery fully charged",
|
||||||
rows = rows.push(modes.width(Length::Fill).align_y(Alignment::Center));
|
_ => "Battery status reported by the system",
|
||||||
} else {
|
})
|
||||||
rows = rows.push(
|
.size(12)
|
||||||
text("Power Profiles service unavailable")
|
.class(theme::Text::Color(muted)),
|
||||||
.size(12)
|
);
|
||||||
.class(theme::Text::Color(muted)),
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut power = row::with_capacity(3).spacing(6).width(Length::Fill);
|
if menu.kind == Menu::Battery {
|
||||||
for (enabled, label, icon, action) in [
|
rows = rows.push(text("Power mode").size(13).class(theme::Text::Color(muted)));
|
||||||
|
if let Some(profiles) = &self.status.power_profiles {
|
||||||
|
let mut modes = row::with_capacity(3).spacing(6).width(Length::Fill);
|
||||||
|
for (index, (label, icon, profile)) in [
|
||||||
(
|
(
|
||||||
self.status.reboot,
|
"Power saver",
|
||||||
"Restart",
|
include_bytes!("../assets/icons/status/power-saver.svg").as_slice(),
|
||||||
include_bytes!("../assets/icons/status/restart.svg").as_slice(),
|
"power-saver",
|
||||||
Action::Reboot,
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
self.status.poweroff,
|
"Balanced",
|
||||||
"Power off",
|
include_bytes!("../assets/icons/status/power-balanced.svg").as_slice(),
|
||||||
include_bytes!("../assets/icons/status/power-off.svg").as_slice(),
|
"balanced",
|
||||||
Action::Poweroff,
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
self.status.suspend,
|
"Performance",
|
||||||
"Suspend",
|
include_bytes!("../assets/icons/status/power-performance.svg")
|
||||||
include_bytes!("../assets/icons/status/suspend.svg").as_slice(),
|
.as_slice(),
|
||||||
Action::Suspend,
|
"performance",
|
||||||
),
|
),
|
||||||
] {
|
]
|
||||||
if enabled {
|
.into_iter()
|
||||||
power = power.push(power_tile_action(label, icon, action, theme, p));
|
.enumerate()
|
||||||
|
{
|
||||||
|
if profiles.available[index] {
|
||||||
|
modes = modes.push(power_tile(
|
||||||
|
label,
|
||||||
|
icon,
|
||||||
|
profile,
|
||||||
|
profiles.active == profile,
|
||||||
|
theme,
|
||||||
|
p,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rows = rows.push(modes);
|
||||||
if self.status.reboot || self.status.poweroff || self.status.suspend {
|
} else {
|
||||||
rows = rows.push(text("Power").size(13).class(theme::Text::Color(muted)));
|
|
||||||
rows = rows.push(power.align_y(Alignment::Center).width(Length::Fill));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !menu.kind.available(&self.status) {
|
|
||||||
rows = rows.push(
|
rows = rows.push(
|
||||||
text("Service unavailable")
|
text("Power Profiles service unavailable")
|
||||||
.size(13)
|
.size(12)
|
||||||
.class(theme::Text::Color(muted)),
|
.class(theme::Text::Color(muted)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut power = row::with_capacity(3).spacing(6).width(Length::Fill);
|
||||||
|
for (enabled, label, icon, action) in [
|
||||||
|
(
|
||||||
|
self.status.reboot,
|
||||||
|
"Restart",
|
||||||
|
include_bytes!("../assets/icons/status/restart.svg").as_slice(),
|
||||||
|
Action::Reboot,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
self.status.poweroff,
|
||||||
|
"Power off",
|
||||||
|
include_bytes!("../assets/icons/status/power-off.svg").as_slice(),
|
||||||
|
Action::Poweroff,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
self.status.suspend,
|
||||||
|
"Suspend",
|
||||||
|
include_bytes!("../assets/icons/status/suspend.svg").as_slice(),
|
||||||
|
Action::Suspend,
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
if enabled {
|
||||||
|
power = power.push(power_tile_action(label, icon, action, theme, p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.status.reboot || self.status.poweroff || self.status.suspend {
|
||||||
|
rows = rows.push(text("Power").size(13).class(theme::Text::Color(muted)));
|
||||||
|
rows = rows.push(power);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !menu.kind.available(&self.status) {
|
||||||
|
rows = rows.push(
|
||||||
|
text("Service unavailable")
|
||||||
|
.size(13)
|
||||||
|
.class(theme::Text::Color(muted)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -921,8 +923,8 @@ fn status_summary<'a>(
|
|||||||
let badge = container(accented_icon(source, 22, primary, accent))
|
let badge = container(accented_icon(source, 22, primary, accent))
|
||||||
.width(32)
|
.width(32)
|
||||||
.height(32)
|
.height(32)
|
||||||
.align_x(alignment::Horizontal::Center)
|
.center_x(32)
|
||||||
.align_y(alignment::Vertical::Center)
|
.center_y(32)
|
||||||
.class(theme::Container::custom(move |_| container::Style {
|
.class(theme::Container::custom(move |_| container::Style {
|
||||||
background: Some(Background::Color(Color {
|
background: Some(Background::Color(Color {
|
||||||
a: if enabled {
|
a: if enabled {
|
||||||
@@ -1138,13 +1140,14 @@ fn power_tile<'a>(
|
|||||||
},
|
},
|
||||||
opacity,
|
opacity,
|
||||||
);
|
);
|
||||||
let tile = column![
|
let tile = container(
|
||||||
bar_icon(icon, 19, foreground),
|
column![bar_icon(icon, 19, foreground), text(label).size(11)]
|
||||||
text(label).size(11).align_x(Alignment::Center)
|
.spacing(3)
|
||||||
]
|
.align_x(Alignment::Center),
|
||||||
.spacing(3)
|
)
|
||||||
.align_x(Alignment::Center);
|
.width(Length::Fill)
|
||||||
let btn = button::custom(tile.align_x(Alignment::Center))
|
.center_x(Length::Fill);
|
||||||
|
let btn = button::custom(tile)
|
||||||
.width(Length::FillPortion(1))
|
.width(Length::FillPortion(1))
|
||||||
.padding([8, 2])
|
.padding([8, 2])
|
||||||
.on_press(cosmic::Action::App(Message::Control(Action::PowerProfile(
|
.on_press(cosmic::Action::App(Message::Control(Action::PowerProfile(
|
||||||
@@ -1162,20 +1165,19 @@ fn power_tile_action<'a>(
|
|||||||
opacity: f32,
|
opacity: f32,
|
||||||
) -> Element<'a, cosmic::Action<Message>> {
|
) -> Element<'a, cosmic::Action<Message>> {
|
||||||
let foreground = color_with_opacity(theme.text_primary, opacity);
|
let foreground = color_with_opacity(theme.text_primary, opacity);
|
||||||
// TODO: confirm column align fix
|
let tile = container(
|
||||||
motion::button(
|
column![bar_icon(icon, 19, foreground), text(label).size(11)]
|
||||||
button::custom(
|
.spacing(3)
|
||||||
column![bar_icon(icon, 19, foreground), text(label).size(11)]
|
.align_x(Alignment::Center),
|
||||||
.spacing(3)
|
)
|
||||||
.align_x(Alignment::Center),
|
.width(Length::Fill)
|
||||||
)
|
.center_x(Length::Fill);
|
||||||
|
let btn = button::custom(tile)
|
||||||
.width(Length::FillPortion(1))
|
.width(Length::FillPortion(1))
|
||||||
.padding([8, 2])
|
.padding([8, 2])
|
||||||
.on_press(cosmic::Action::App(Message::ConfirmPower(action))),
|
.on_press(cosmic::Action::App(Message::ConfirmPower(action)));
|
||||||
foreground,
|
|
||||||
false,
|
motion::button(btn, foreground, false, opacity)
|
||||||
opacity,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calendar_month(today: jiff::civil::Date, offset: i32) -> (jiff::civil::Date, usize, usize) {
|
fn calendar_month(today: jiff::civil::Date, offset: i32) -> (jiff::civil::Date, usize, usize) {
|
||||||
|
|||||||
Reference in new issue
Block a user