Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
280 changes: 162 additions & 118 deletions src/ui/components/left_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::ui::theme::{DashColors, Shadow, Shape, Spacing};
use dash_sdk::dashcore_rpc::dashcore::Network;
use eframe::epaint::Margin;
use egui::{Color32, Context, Frame, ImageButton, RichText, SidePanel, TextureHandle};
use egui_extras::{Size, StripBuilder};
use rust_embed::RustEmbed;
use std::sync::Arc;

Expand Down Expand Up @@ -109,127 +110,170 @@ pub fn add_left_panel(
.corner_radius(egui::CornerRadius::same(Shape::RADIUS_LG))
.shadow(Shadow::elevated())
.show(ui, |ui| {
ui.vertical_centered(|ui| {
for (label, screen_type, icon_path) in buttons.iter() {
let texture: Option<TextureHandle> = load_icon(ctx, icon_path);
let is_selected = selected_screen == *screen_type;

let button_color = if is_selected {
Color32::WHITE // Bright white for selected
} else if dark_mode {
Color32::from_rgb(180, 180, 180) // Bright gray for visibility in dark mode
} else {
Color32::from_rgb(160, 160, 160) // Medium gray for contrast in light mode
};

// Add icon-based button if texture is loaded
if let Some(ref texture) = texture {
let button =
ImageButton::new(texture).frame(false).tint(button_color);

let added = ui.add(button);
if added.clicked() {
action =
AppAction::SetMainScreenThenGoToMainScreen(*screen_type);
} else if added.hovered() {
ui.ctx().set_cursor_icon(egui::CursorIcon::PointingHand);
}
// Put the label beneath the icon
let color = if is_selected {
DashColors::DASH_BLUE
} else {
DashColors::text_primary(dark_mode)
};
let label_text = RichText::new(*label).color(color).size(13.0);
ui.label(label_text);
} else {
// Fallback to a modern gradient button if texture loading fails
if is_selected {
if GradientButton::new(*label, app_context)
.min_width(60.0)
.glow()
.show(ui)
.clicked()
{
action = AppAction::SetMainScreen(*screen_type);
}
} else {
let button = egui::Button::new(*label)
.fill(DashColors::glass_white(dark_mode))
.stroke(egui::Stroke::new(
1.0,
DashColors::glass_border(dark_mode),
))
.corner_radius(egui::CornerRadius::same(Shape::RADIUS_MD))
.min_size(egui::vec2(60.0, 60.0));

if ui.add(button).clicked() {
action = AppAction::SetMainScreen(*screen_type);
}
}
}

ui.add_space(Spacing::MD); // Add some space between buttons
}

// Push content to the top and dev label + logo to the bottom
ui.with_layout(egui::Layout::bottom_up(egui::Align::Center), |ui| {
if app_context.is_developer_mode() {
ui.add_space(Spacing::MD);
let dev_label = egui::RichText::new("🔧 Dev mode")
.color(DashColors::GRADIENT_PURPLE)
.size(12.0);
if ui.label(dev_label).clicked() {
action = AppAction::SetMainScreenThenGoToMainScreen(
RootScreenType::RootScreenNetworkChooser,
);
};
}

// Show network name if not on main Dash network
if app_context.network != Network::Dash {
let (network_name, network_color) = match app_context.network {
Network::Testnet => ("Testnet", Color32::from_rgb(255, 165, 0)),
Network::Devnet => ("Devnet", Color32::DARK_RED),
Network::Regtest => {
("Local Network", Color32::from_rgb(139, 69, 19))
}
_ => ("Unknown", DashColors::DASH_BLUE),
};

ui.label(
RichText::new(network_name)
.color(network_color)
.size(12.0)
.strong(),
);
ui.add_space(2.0);
}

// Add Dash logo at the bottom
if let Some(dash_texture) = load_icon(ctx, "dash.png") {
if app_context.network == Network::Dash {
ui.add_space(Spacing::SM);
}
let logo_size = egui::vec2(50.0, 20.0); // Even smaller size, same aspect ratio
let logo_response = ui.add(
egui::Image::new(&dash_texture)
.fit_to_exact_size(logo_size)
.texture_options(egui::TextureOptions::LINEAR) // Smooth interpolation to reduce pixelation
.sense(egui::Sense::click()),
);
// Reserve a fixed area at the bottom for the logo and labels,
// and make the button list above it vertically scrollable.
let mut bottom_reserved = Spacing::SM + 20.0; // spacing + logo height
if app_context.network != Network::Dash {
bottom_reserved += 22.0; // network label + spacing
}
if app_context.is_developer_mode() {
bottom_reserved += Spacing::MD + 16.0; // dev label area
}

StripBuilder::new(ui)
.size(Size::remainder()) // top: fills remaining height
.size(Size::exact(bottom_reserved.max(40.0))) // bottom: reserved area
.vertical(|mut strip| {
// Top cell: scrollable list of buttons
strip.cell(|ui| {
egui::ScrollArea::vertical()
.id_salt("left_panel_buttons_scroll")
.auto_shrink([false, false])
.show(ui, |ui| {
ui.vertical_centered(|ui| {
for (label, screen_type, icon_path) in buttons.iter() {
let texture: Option<TextureHandle> = load_icon(ctx, icon_path);
let is_selected = selected_screen == *screen_type;

let button_color = if is_selected {
Color32::WHITE
} else if dark_mode {
Color32::from_rgb(180, 180, 180)
} else {
Color32::from_rgb(160, 160, 160)
};

if let Some(ref texture) = texture {
let button = ImageButton::new(texture)
.frame(false)
.tint(button_color);

let added = ui.add(button);
if added.clicked() {
action = AppAction::SetMainScreenThenGoToMainScreen(
*screen_type,
);
} else if added.hovered() {
ui.ctx().set_cursor_icon(
egui::CursorIcon::PointingHand,
);
}
// Put the label beneath the icon
let color = if is_selected {
DashColors::DASH_BLUE
} else {
DashColors::text_primary(dark_mode)
};
let label_text =
RichText::new(*label).color(color).size(13.0);
ui.label(label_text);
} else {
// Fallback button if texture not available
if is_selected {
if GradientButton::new(*label, app_context)
.min_width(60.0)
.glow()
.show(ui)
.clicked()
{
action = AppAction::SetMainScreen(*screen_type);
}
} else {
let button = egui::Button::new(*label)
.fill(DashColors::glass_white(dark_mode))
.stroke(egui::Stroke::new(
1.0,
DashColors::glass_border(dark_mode),
))
.corner_radius(egui::CornerRadius::same(
Shape::RADIUS_MD,
))
.min_size(egui::vec2(60.0, 60.0));

if ui.add(button).clicked() {
action = AppAction::SetMainScreen(*screen_type);
}
}
}

if logo_response.clicked() {
ui.ctx()
.open_url(egui::OpenUrl::new_tab("https://dash.org"));
}
ui.add_space(Spacing::MD);
}
});
});
});

if logo_response.hovered() {
ui.ctx().set_cursor_icon(egui::CursorIcon::PointingHand);
}
}
// Bottom cell: always visible logo and labels
strip.cell(|ui| {
ui.with_layout(
egui::Layout::bottom_up(egui::Align::Center),
|ui| {
// Dash logo at the very bottom
if let Some(dash_texture) = load_icon(ctx, "dash.png") {
if app_context.network == Network::Dash {
ui.add_space(Spacing::SM);
}
let logo_size = egui::vec2(50.0, 20.0);
let logo_response = ui.add(
egui::Image::new(&dash_texture)
.fit_to_exact_size(logo_size)
.texture_options(egui::TextureOptions::LINEAR)
.sense(egui::Sense::click()),
);

if logo_response.clicked() {
ui.ctx()
.open_url(egui::OpenUrl::new_tab("https://dash.org"));
}

if logo_response.hovered() {
ui.ctx()
.set_cursor_icon(egui::CursorIcon::PointingHand);
}
}

// Network label (if not on mainnet)
if app_context.network != Network::Dash {
let (network_name, network_color) = match app_context.network {
Network::Testnet => (
"Testnet",
Color32::from_rgb(255, 165, 0),
),
Network::Devnet => (
"Devnet",
Color32::DARK_RED,
),
Network::Regtest => (
"Local Network",
Color32::from_rgb(139, 69, 19),
),
_ => ("Unknown", DashColors::DASH_BLUE),
};

ui.add_space(2.0);
ui.label(
RichText::new(network_name)
.color(network_color)
.size(12.0)
.strong(),
);
}

// Dev mode label (above network label if present)
if app_context.is_developer_mode() {
ui.add_space(Spacing::MD);
let dev_label = egui::RichText::new("🔧 Dev mode")
.color(DashColors::GRADIENT_PURPLE)
.size(12.0);
if ui.label(dev_label).clicked() {
action = AppAction::SetMainScreenThenGoToMainScreen(
RootScreenType::RootScreenNetworkChooser,
);
}
}
},
);
});
});
});
}); // Close the island frame
});

Expand Down
Loading