skills ui revamp
Guide for skills ui revamp
Skills Tab UI Revamp
Purpose
This document describes the redesign of the Skills tab UI from a large-tile scrollable layout to a compact OSRS-style grid layout with detailed tooltips.
Problem Statement
Original Issues
- Oversized Tiles: Skill cells were too large, requiring a scrollbar
- Inefficient Space Usage: Only 3 skills visible at once in a 210px wide panel
- Redundant Text: Skill names displayed on every tile wasted space
- Missing XP Details: No way to see XP progress without opening a separate menu
- Incorrect Icons: Crafting and Fletching used placeholder icons instead of proper assets
User Requirements
- Compact grid layout matching OSRS style
- Show "Level/Level" format (current/base) instead of "Lvl X"
- Remove skill names from tiles, show in tooltips instead
- Tooltips should show: skill name, current XP, XP to next level, XP to target (future)
- Remove Agility and Thieving (not in this game)
- Rename "Prayer" to "Tapasya" (Indian mythology branding)
- Use correct icons for Crafting and Fletching
Implementation
File: src/systems/ui/skills_ui.rs
Old Layout (Scrollable, Large Tiles)
egui::ScrollArea::vertical().show(ui, |ui| {
egui::Grid::new("stats_grid")
.spacing(egui::vec2(10.0, 10.0))
.min_col_width(60.0)
.show(ui, |ui| {
for (i, (skill, name, _icon_handle)) in skill_list_grid.iter().enumerate() {
let level = skills.get_level(*skill);
let xp = skills.get_xp(*skill);
render_skill_cell(ui, name, level as u8, xp as i32, tex_id);
if (i + 1) % 3 == 0 {
ui.end_row();
}
}
});
});
fn render_skill_cell(ui: &mut egui::Ui, name: &str, level: u8, xp: i32, tex_id: egui::TextureId) {
let frame = egui::Frame::none()
.inner_margin(4.0)
.fill(egui::Color32::from_rgb(50, 45, 40))
.rounding(4.0)
.stroke(egui::Stroke::new(1.0, egui::Color32::from_rgb(80, 75, 70)));
frame.show(ui, |ui| {
ui.vertical_centered(|ui| {
ui.image(egui::load::SizedTexture::new(tex_id, [24.0, 24.0]));
ui.label(egui::RichText::new(format!("Lvl {}", level)).strong().color(egui::Color32::YELLOW));
ui.label(egui::RichText::new(name).size(10.0)); // Wasted space
}).response.on_hover_text(format!("XP: {}", xp)); // Minimal tooltip
});
}Problems:
- Vertical layout with large icon + text + skill name
- Simple tooltip with only XP
- ScrollArea required for 18 skills
New Layout (Compact, No Scroll)
// Main Stats Container - No Scroll Area needed if it fits
// OSRS style: Black background, tight grid
egui::Frame::none()
.fill(egui::Color32::from_rgb(20, 20, 20))
.inner_margin(4.0)
.show(ui, |ui| {
ui.horizontal(|ui| {
ui.heading("Skills");
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
let total_level: i64 = skill_list_grid.iter().map(|(s, _, _)| skills.get_level(*s)).sum();
ui.label(egui::RichText::new(format!("Total Level: {}", total_level)).color(egui::Color32::GOLD));
});
});
ui.separator();
ui.add_space(4.0);
let cell_size = egui::vec2(62.0, 30.0); // Compact size
egui::Grid::new("stats_grid_compact")
.spacing(egui::vec2(4.0, 4.0))
.show(ui, |ui| {
for (i, (skill, name, _)) in skill_list_grid.iter().enumerate() {
let level = skills.get_level(*skill);
let xp = skills.get_xp(*skill);
let boosted_level = level; // TODO: Get boosted level when implemented
let tex_id = skill_textures.get(skill).copied().unwrap_or(egui::TextureId::default());
render_compact_skill_cell(ui, name, boosted_level as u8, level as u8, xp, tex_id, cell_size);
if (i + 1) % 3 == 0 {
ui.end_row();
}
}
});
});
fn render_compact_skill_cell(
ui: &mut egui::Ui,
minified_name: &str,
boosted: u8,
base: u8,
xp: i64,
tex_id: egui::TextureId,
size: egui::Vec2
) {
let frame = egui::Frame::none()
.fill(egui::Color32::from_rgb(45, 40, 35))
.stroke(egui::Stroke::new(1.0, egui::Color32::from_rgb(0, 0, 0))) // Black border
.rounding(2.0)
.inner_margin(2.0);
let response = frame.show(ui, |ui| {
ui.set_min_size(size);
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing = egui::vec2(2.0, 0.0);
// Left: Icon
ui.image(egui::load::SizedTexture::new(tex_id, [20.0, 20.0]));
// Right: Level/Level
ui.vertical(|ui| {
ui.label(
egui::RichText::new(format!("{}/{}", boosted, base))
.color(egui::Color32::YELLOW)
.size(12.0)
);
});
});
}).response;
// Rich Tooltip
if response.hovered() {
response.on_hover_ui(|ui| {
ui.heading(minified_name);
ui.separator();
ui.label(format!("Current XP: {}", xp));
let next_lvl_xp = Skills::get_xp_for_level((base as i64) + 1);
if base < 99 {
ui.label(format!("Next Level at: {}", next_lvl_xp));
ui.label(format!("Remaining: {}", next_lvl_xp - xp));
} else {
ui.label("Max Level Reached");
}
});
}
}Improvements:
- Horizontal layout: Icon + Level/Level (62x30px cells)
- No skill name on tile (shown in tooltip)
- Rich tooltip with XP progress details
- No ScrollArea needed (all skills fit)
- Dark background (OSRS style)
Skill Grid Layout
Old Grid (18 skills with Agility/Thieving)
Attack Hitpoints Mining
Strength Agility Smithing
Defence Ayurveda Fishing
Ranged Thieving Cooking
Prayer Crafting Farming
Magic Fletching WoodcuttingNew Grid (18 skills, removed Agility/Thieving, added Firemaking/Slayer)
Attack Hitpoints Mining
Strength Smithing Fishing
Defence Ayurveda Cooking
Ranged Crafting Firemaking
Tapasya Farming Magic
Fletching Woodcutting SlayerChanges:
- Removed: Agility, Thieving
- Renamed: Prayer → Tapasya
- Added: Firemaking, Slayer (to fill grid)
Tooltip Implementation
Incorrect Approach (Compilation Error)
// WRONG: show_tooltip_text signature changed in egui 0.28
egui::show_tooltip_text(ui.ctx(), egui::Id::new(minified_name), |ui| {
ui.heading(minified_name);
// ...
});Error:
error[E0061]: this function takes 4 arguments but 3 arguments were suppliedCorrect Approach
// CORRECT: Use response.on_hover_ui
if response.hovered() {
response.on_hover_ui(|ui| {
ui.heading(minified_name);
ui.separator();
ui.label(format!("Current XP: {}", xp));
let next_lvl_xp = Skills::get_xp_for_level((base as i64) + 1);
if base < 99 {
ui.label(format!("Next Level at: {}", next_lvl_xp));
ui.label(format!("Remaining: {}", next_lvl_xp - xp));
} else {
ui.label("Max Level Reached");
}
});
}Why this works:
response.on_hover_uiis the idiomatic egui 0.28 pattern- Automatically handles tooltip positioning and lifecycle
- Closure receives
&mut Uifor custom content
Icon Loading
File: src/systems/ui/mod.rs
Added Fields to UiAssets:
pub struct UiAssets {
// ... existing fields
pub crafting_icon: Handle<Image>, // NEW
pub fletching_icon: Handle<Image>, // NEW
}Load Proper Icons:
fn load_ui_assets(mut ui_assets: ResMut<UiAssets>, asset_server: Res<AssetServer>) {
// ... existing loads
// [FIX] Proper icons
ui_assets.crafting_icon = asset_server.load("logos/Crafting_icon.png");
ui_assets.fletching_icon = asset_server.load("logos/Fletching_icon.png");
}Use in Texture Map:
// OLD: Placeholder
skill_textures.insert(SkillType::Crafting, stats_tex); // Placeholder
skill_textures.insert(SkillType::Fletching, stats_tex); // Placeholder
// NEW: Proper icons
skill_textures.insert(SkillType::Crafting, contexts.add_image(ui_assets.crafting_icon.clone()));
skill_textures.insert(SkillType::Fletching, contexts.add_image(ui_assets.fletching_icon.clone()));Type Unification
Problem: Duplicate AttackStyle Enum
The UI module had its own
AttackStyle enum that duplicated combat::Style:// src/systems/ui/mod.rs (OLD)
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Resource)]
pub enum AttackStyle {
#[default]
Accurate,
Aggressive,
Defensive,
Controlled,
}This caused type mismatches when the Combat UI tried to use
combat::Style.Solution: Type Alias
// src/systems/ui/mod.rs (NEW)
// Use Combat System Style instead of local enum
pub use crate::systems::combat::Style as AttackStyle; // Alias for compatibilityBenefits:
- Single source of truth for combat styles
- No type conversion needed
- Easier to extend with new styles (e.g.,
Longrange,Rapid)
Compilation Issues Encountered
Issue 1: Derive on Type Alias
#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Resource)]
pub use crate::systems::combat::Style as AttackStyle;Error:
error[E0774]: `derive` may only be applied to `struct`s, `enum`s and `union`sFix: Remove
#[derive] from type alias (traits come from original Style enum)Issue 2: Missing Default Implementation
#[derive(Resource, Default)]
pub struct UiState {
pub selected_attack_style: AttackStyle, // = combat::Style
}Error:
error[E0277]: the trait bound `Style: Default` is not satisfiedFix: Add
#[derive(Default)] to combat::Style enum:// src/systems/combat/mod.rs
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum Style {
#[default]
Accurate,
Aggressive,
Defensive,
Controlled,
Longrange,
}Issue 3: Color32::black() Not Found
.stroke(egui::Stroke::new(1.0, egui::Color32::black()))Error:
error[E0599]: no function or associated item named `black` found for struct `Color32`Fix: Use
Color32::from_rgb(0, 0, 0) instead:.stroke(egui::Stroke::new(1.0, egui::Color32::from_rgb(0, 0, 0)))Design Rationale
Why Compact Layout?
- Space Efficiency: 18 skills fit in 210px width without scrolling
- OSRS Familiarity: Players expect this layout from RuneScape
- Information Density: More skills visible at once
Why Level/Level Format?
- Boosted Levels: Prepares for future potion/prayer boosts
- Visual Clarity: Immediately shows if level is boosted (e.g., "105/99")
- OSRS Standard: Matches player expectations
Why Tooltips for Skill Names?
- Space Savings: Removes 10-15px of vertical space per cell
- Cleaner Look: Icons + numbers are more compact
- Rich Information: Tooltips can show much more than just the name
Why Remove Agility/Thieving?
- Game Design: These skills don't fit the Indian mythology theme
- Scope Management: Fewer skills = more focused gameplay
Why Rename Prayer to Tapasya?
- Cultural Authenticity: Tapasya is the Sanskrit term for spiritual discipline
- Branding: Differentiates from OSRS while maintaining similar mechanics
Testing Checklist
- All 18 skills display in 3-column grid
- No scrollbar appears
- Level/Level format shows correctly
- Tooltips appear on hover
- Tooltips show: skill name, current XP, XP to next level
- "Tapasya" appears instead of "Prayer"
- Agility and Thieving are not displayed
- Crafting icon loads correctly (not placeholder)
- Fletching icon loads correctly (not placeholder)
- Total Level displays in top-right corner
- Grid spacing is tight (4px)
- Cell size is 62x30px
- Background is dark (OSRS style)
Future Enhancements
- Boosted Levels: Implement actual boosted level tracking for potions/prayers
- XP Target: Allow players to set target levels, show "XP to Target" in tooltip
- Skill Ordering: Make grid order configurable (player preference)
- Color Coding: Highlight boosted levels in green, debuffed in red
- Progress Bar: Show XP progress bar in tooltip
- Skill Icons: Create custom icons for all skills (remove placeholders)
- Virtual Levels: Show levels beyond 99 (e.g., "120/99" for 200M XP)
Related Files
src/systems/ui/skills_ui.rs: Skills tab renderingsrc/systems/ui/mod.rs: Icon loading and type aliasessrc/systems/player/skills.rs: Skills component and XP calculationsassets/logos/*.png: Skill icons