OneLauncher-Cracked/packages/oneclient_app/src/components/button.rs
oiupoyt d214afe99b
Some checks are pending
Release Multiplatform Binaries / build (deb,appimage, linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-gnu) (push) Waiting to run
Release Multiplatform Binaries / build (dmg,app, macos-arm64, macos-14, aarch64-apple-darwin) (push) Waiting to run
Release Multiplatform Binaries / build (dmg,app, macos-x86_64, macos-14, x86_64-apple-darwin) (push) Waiting to run
Release Multiplatform Binaries / build (nsis, windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Waiting to run
Release Multiplatform Binaries / create-release (push) Blocked by required conditions
feat: update base to official upstream 2.5.0 with all cracked features intact (2.5.0-c1)
- Rebased base onto official Polyfrost OneLauncher 2.5.0 (release v2.5.0 + latest bundle fixes).
- Integrated new Upstream Control Center quick settings & system info overlay and navbar avatar-with-gear trigger.
- Preserved top navbar 'Skins' direct route navigation.
- Maintained guaranteed offline mode launch without requiring any Microsoft accounts.
- Maintained custom 3D offline skin changer, player model switch (Classic/Slim), and targeted Minecraft Java UUID character slot isolation.
- Maintained CustomSkinLoader auto-configuration for local skins, Ely.by, and SkinRestorer.
- Integrated direct GitHub Releases auto-updater pointing to oiupoyt/OneLauncher-Cracked.
- Preserved inline username renaming modal and Change Skin navigation in account settings and onboarding.
- Maintained mods filter popover blur/text ghosting fix in package manager.
- Preserved fast dev profile and multiplatform release workflow for v2.5.0-c1.
- All workspace tests and clippy checks pass with 0 errors.
2026-09-17 07:15:57 +05:30

480 lines
13 KiB
Rust

#![allow(dead_code)]
use std::borrow::Cow;
use freya::prelude::*;
use crate::{
components::{Icon, IconTint, IconType, TooltipPlacement, tooltip::use_tooltip_anchor},
theme::colors,
};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ButtonVariant {
#[default]
Primary,
Secondary,
Danger,
Ghost,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ButtonSize {
Small,
#[default]
Medium,
Large,
Icon,
}
const DISABLED_OPACITY: f32 = 0.5;
struct ButtonColors {
background: Color,
hover_background: Color,
pressed_background: Color,
disabled_background: Color,
foreground: Color,
disabled_foreground: Color,
border: Option<Color>,
hover_border: Option<Color>,
pressed_border: Option<Color>,
focus_border: Option<Color>,
}
#[derive(Clone, PartialEq)]
pub struct Button {
size: ButtonSize,
variant: ButtonVariant,
layout: LayoutData,
style: StyleState,
text_style: TextStyleData,
enabled: bool,
focusable: bool,
alt: Option<Box<str>>,
tooltip: Option<Box<str>>,
tooltip_placement: TooltipPlacement,
on_press: Option<EventHandler<Event<PressEventData>>>,
overflow: Overflow,
elements: Vec<Element>,
key: DiffKey,
cursor_icon: CursorIcon,
}
impl Default for Button {
fn default() -> Self {
Self::new()
}
}
impl Button {
pub fn new() -> Self {
let size = ButtonSize::default();
let variant = ButtonVariant::default();
let mut layout = LayoutData::default();
let mut style = StyleState::default();
let mut text_style = TextStyleData::default();
let (padding, default_radius, font_size, width, height) = size_layout(size);
let colors = variant_colors(variant);
layout.padding = padding;
layout.width = width;
layout.height = height;
layout.content = Content::Flex;
layout.cross_alignment = Alignment::Center;
layout.main_alignment = Alignment::Center;
layout.direction = Direction::Horizontal;
layout.spacing.0 = 4.;
style.background = Fill::Color(colors.background);
if let Some(border_color) = colors.border {
style.borders.push(
Border::new()
.fill(border_color)
.width(1.)
.alignment(BorderAlignment::Inner),
);
}
style.corner_radius = default_radius;
text_style.font_size = Some(font_size.into());
Self {
size,
layout,
style,
text_style,
variant,
enabled: true,
focusable: true,
alt: None,
tooltip: None,
tooltip_placement: TooltipPlacement::default(),
on_press: None,
elements: Vec::new(),
overflow: Overflow::Clip,
key: DiffKey::None,
cursor_icon: CursorIcon::Pointer,
}
}
pub fn variant(mut self, variant: ButtonVariant) -> Self {
self.variant = variant;
self
}
pub fn primary(self) -> Self {
self.variant(ButtonVariant::Primary)
}
pub fn secondary(self) -> Self {
self.variant(ButtonVariant::Secondary)
}
pub fn danger(self) -> Self {
self.variant(ButtonVariant::Danger)
}
pub fn ghost(self) -> Self {
self.variant(ButtonVariant::Ghost)
}
pub fn size(mut self, size: ButtonSize) -> Self {
self.size = size;
let (padding, corner_radius, font_size, width, height) = size_layout(size);
self.layout.width = width;
self.layout.height = height;
self.layout.padding = padding;
self.style.corner_radius = corner_radius;
self.text_style.font_size = Some(font_size.into());
self
}
pub fn overflow(mut self, overflow: Overflow) -> Self {
self.overflow = overflow;
self
}
pub fn font_size(mut self, font_size: impl Into<f32>) -> Self {
self.text_style.font_size = Some(font_size.into().into());
self
}
pub fn font_weight(mut self, weight: FontWeight) -> Self {
self.text_style.font_weight = Some(weight);
self
}
pub fn small(self) -> Self {
self.size(ButtonSize::Small)
}
pub fn medium(self) -> Self {
self.size(ButtonSize::Medium)
}
pub fn large(self) -> Self {
self.size(ButtonSize::Large)
}
pub fn icon(self) -> Self {
self.size(ButtonSize::Icon)
}
pub fn enabled(mut self, enabled: impl Into<bool>) -> Self {
self.enabled = enabled.into();
self
}
pub fn focusable(mut self, focusable: impl Into<bool>) -> Self {
self.focusable = focusable.into();
self
}
pub fn disabled(self, disabled: bool) -> Self {
self.enabled(!disabled)
}
pub fn on_press(mut self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
self.on_press = Some(on_press.into());
self
}
/// Accessible name for icon-only buttons which have no label to read out
pub fn alt(mut self, alt: impl Into<Box<str>>) -> Self {
self.alt = Some(alt.into());
self
}
pub fn tooltip(mut self, tooltip: impl Into<Box<str>>) -> Self {
let tooltip = tooltip.into();
if self.alt.is_none() {
self.alt = Some(tooltip.clone());
}
self.tooltip = Some(tooltip);
self
}
pub fn tooltip_placement(mut self, placement: TooltipPlacement) -> Self {
self.tooltip_placement = placement;
self
}
pub fn cursor_icon(mut self, cursor_icon: CursorIcon) -> Self {
self.cursor_icon = cursor_icon;
self
}
pub fn text(self, text: impl Into<Cow<'static, str>>) -> Self {
self.child(label().text(text))
}
}
impl ChildrenExt for Button {
fn get_children(&mut self) -> &mut Vec<Element> {
&mut self.elements
}
}
impl KeyExt for Button {
fn write_key(&mut self) -> &mut DiffKey {
&mut self.key
}
}
impl LayoutExt for Button {
fn get_layout(&mut self) -> &mut LayoutData {
&mut self.layout
}
}
impl ContainerExt for Button {}
impl StyleExt for Button {
fn get_style(&mut self) -> &mut StyleState {
&mut self.style
}
}
impl Component for Button {
fn render(&self) -> impl IntoElement {
let mut hovering = use_state(|| false);
let mut pressing = use_state(|| false);
let a11y_id = use_a11y();
let focus = use_focus(a11y_id);
let tooltip = use_tooltip_anchor(self.tooltip.clone(), self.tooltip_placement);
let enabled = use_reactive(&self.enabled);
let cursor_icon = self.cursor_icon;
let palette = variant_colors(self.variant);
let background = if !enabled() {
palette.disabled_background
} else if pressing() {
palette.pressed_background
} else if hovering() {
palette.hover_background
} else {
palette.background
};
let foreground = if enabled() {
palette.foreground
} else {
palette.disabled_foreground
};
// Child icons would otherwise inherit this through a `styled` event that lands a couple of
// frames after the button paints, so hand it straight to them instead
let mut icon_tint = use_state(|| foreground);
icon_tint.set_if_modified(foreground);
use_hook(|| provide_context(IconTint(icon_tint)));
let border_color = if !enabled() {
palette.border
} else if pressing() {
palette.pressed_border
} else if hovering() {
palette.hover_border
} else if focus().is_focused() {
palette.focus_border.or(Some(Color::WHITE))
} else {
palette.border
};
let on_press = self.on_press.clone();
let mut rect = rect()
.a11y_id(a11y_id)
.a11y_focusable(enabled() && self.focusable)
.a11y_role(AccessibilityRole::Button)
.map(self.alt.clone(), |rect, alt| rect.a11y_alt(alt))
.overflow(self.overflow)
.layout(self.layout.clone())
.text_style(self.text_style.clone())
.children(self.elements.clone());
rect.get_style().clone_from(&self.style);
rect = rect.background(background).color(foreground);
if !enabled() {
rect = rect.opacity(DISABLED_OPACITY);
}
if let Some(border) = border_color {
rect = rect.border(
Border::new()
.fill(border)
.width(1.)
.alignment(BorderAlignment::Inner),
);
}
if enabled() {
rect = rect
.on_pointer_down(move |_| pressing.set(true))
.on_global_pointer_press(move |_| pressing.set(false))
.on_pointer_over(move |_| hovering.set(true))
.on_pointer_out(move |_| {
hovering.set(false);
pressing.set(false);
})
.cursor(cursor_icon)
.map(on_press.clone(), |rect, handler| {
rect.on_press(move |event: Event<PressEventData>| {
handler.call(event);
})
});
} else {
rect = rect.cursor(CursorIcon::NotAllowed);
}
tooltip.attach(rect)
}
fn render_key(&self) -> DiffKey {
self.key.clone().or(self.default_key())
}
}
fn variant_colors(variant: ButtonVariant) -> ButtonColors {
match variant {
ButtonVariant::Primary => ButtonColors {
background: colors::brand(),
hover_background: colors::brand_hover(),
pressed_background: colors::brand_pressed(),
disabled_background: colors::brand_disabled(),
foreground: colors::fg_primary(),
disabled_foreground: colors::fg_primary_disabled(),
border: None,
hover_border: None,
pressed_border: None,
focus_border: None,
},
ButtonVariant::Secondary => ButtonColors {
background: colors::component_bg(),
hover_background: colors::component_bg_hover(),
pressed_background: colors::component_bg_pressed(),
disabled_background: colors::component_bg_disabled(),
foreground: colors::fg_primary(),
disabled_foreground: colors::fg_primary_disabled(),
border: Some(colors::component_border()),
hover_border: Some(colors::component_border_hover()),
pressed_border: Some(colors::component_border_pressed()),
focus_border: Some(colors::brand()),
},
ButtonVariant::Danger => ButtonColors {
background: colors::danger(),
hover_background: colors::danger_hover(),
pressed_background: colors::danger_pressed(),
disabled_background: colors::danger_disabled(),
foreground: colors::fg_primary(),
disabled_foreground: colors::fg_primary_disabled(),
border: None,
hover_border: None,
pressed_border: None,
focus_border: None,
},
ButtonVariant::Ghost => ButtonColors {
background: Color::TRANSPARENT,
hover_background: colors::ghost_overlay_hover(),
pressed_background: colors::ghost_overlay_pressed(),
disabled_background: Color::TRANSPARENT,
foreground: colors::fg_primary(),
disabled_foreground: colors::fg_primary_disabled(),
border: None,
hover_border: None,
pressed_border: None,
focus_border: None,
},
}
}
fn size_layout(size: ButtonSize) -> (Gaps, CornerRadius, f32, Size, Size) {
match size {
ButtonSize::Small => (
Gaps::new_symmetric(4., 8.),
CornerRadius::new_all(6.),
12.,
Size::auto(),
Size::auto(),
),
ButtonSize::Medium => (
Gaps::new_symmetric(6., 12.),
CornerRadius::new_all(8.),
14.,
Size::auto(),
Size::auto(),
),
ButtonSize::Large => (
Gaps::new_symmetric(8., 32.),
CornerRadius::new_all(10.),
18.,
Size::auto(),
Size::auto(),
),
ButtonSize::Icon => (
Gaps::new_all(6.),
CornerRadius::new_all(8.),
14.,
Size::px(32.),
Size::px(32.),
),
}
}
pub fn link_button() -> Button {
Button::new()
.primary()
.child(
Icon::new(IconType::LinkExternal01)
.size(14.)
.color(colors::fg_primary()),
)
.child(label().text("Open"))
}
pub fn open_folder_button(folder: std::path::PathBuf) -> Button {
Button::new()
.secondary()
.icon()
.tooltip("Open folder")
.on_press(move |_| {
std::fs::create_dir_all(&folder).ok();
crate::platform::open_path(&folder.to_string_lossy());
})
.child(Icon::new(IconType::Folder).size(16.))
}