Skip to content
Open
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
63 changes: 31 additions & 32 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ use swiftlib::{
process,
task::yield_now,
};
use viewkit::{ipc_proto, render_component_to_pixmap, Window, VComponent};
use viewkit::{ipc_proto, render_component_to_pixmap, VComponent, Window};

fn main() {
println!("[Dock] start");
println!("[Dock] listing apps...");
let apps = list_app_bundles();

println!("[Dock] apps count={}", apps.len());
let (width, height) = dock_window_size(apps.len());

let (width, height) = dock_window_size(apps.len());
let mut window = match Window::new(width, height, ipc_proto::LAYER_STATUS) {
Ok(w) => w,
Err(e) => {
Expand All @@ -24,9 +23,9 @@ fn main() {
println!("[Dock] window ready id={}", window.id());

let mut sel = 0usize;

println!("[Dock] rendering...");
let dock = render_dock_component(&apps, sel);

println!("[Dock] presenting...");
let pixels = render_component_to_pixmap(&dock, width as u32, height as u32);
if let Err(e) = window.present(&pixels) {
Expand All @@ -35,36 +34,39 @@ fn main() {
}
println!("[Dock] shown");

// 描画ロジックの重複を避けるためのクロージャ
let mut redraw = |current_sel: usize| {
let dock = render_dock_component(&apps, current_sel);
let pixels = render_component_to_pixmap(&dock, width as u32, height as u32);
let _ = window.present(&pixels);
};

loop {
let sc_opt = match read_scancode_tap() {
Ok(Some(sc)) => Some(sc),
Ok(None) => read_scancode(),
Err(_) => read_scancode(),
};

if let Some(sc) = sc_opt {
// ESC
if sc == 0x01 || sc == 0x81 {
println!("[Dock] exit");
return;
}

// Left arrow (press)
if sc == 0x4B {
if sel > 0 {
sel -= 1;
}
let dock = render_dock_component(&apps, sel);
let pixels = render_component_to_pixmap(&dock, width as u32, height as u32);
let _ = window.present(&pixels);
if sc == 0x4B && sel > 0 {
sel -= 1;
redraw(sel);
}

// Right arrow (press)
if sc == 0x4D {
if sel + 1 < apps.len() {
sel += 1;
}
let dock = render_dock_component(&apps, sel);
let pixels = render_component_to_pixmap(&dock, width as u32, height as u32);
let _ = window.present(&pixels);
if sc == 0x4D && sel + 1 < apps.len() {
sel += 1;
redraw(sel);
}

// Enter (press)
if sc == 0x1C {
if let Some((app, _icon)) = apps.get(sel) {
Expand All @@ -85,24 +87,22 @@ fn render_dock_component(
selected: usize,
) -> VComponent {
let appicon_template = include_str!("components/appicon.html");

let mut icons = Vec::new();

for (i, (name, icon_opt)) in apps.iter().enumerate() {
let mut appicon = VComponent::from_str(appicon_template);

if let Some(path) = icon_opt {
println!("[Dokc] loading icon for {} from {}", name, path);
println!("[Dock] loading icon for {} from {}", name, path); // タイポを修正
appicon = appicon.image(path.clone());
} else {
println!("[Dock] no icon for {}, using default", name);
let label = name.trim_end_matches(".app");
appicon = appicon.text(label.to_string());
}

if i == selected {
appicon = appicon.class("selected");
}

icons.push(appicon);
}

Expand All @@ -114,11 +114,13 @@ fn dock_window_size(app_count: usize) -> (u16, u16) {
let icon_width = 40usize;
let gap = 10usize;
let padding = 18usize;

let content_width = if app_count == 0 {
0
} else {
app_count * icon_width + app_count.saturating_sub(1) * gap
};

let width = content_width + padding * 2;
(width.max(120).min(u16::MAX as usize) as u16, 75)
}
Expand All @@ -133,32 +135,28 @@ fn read_file(path: &str, max_size: usize) -> Option<Vec<u8>> {
fn list_app_bundles() -> Vec<(String, Option<String>)> {
let mut apps = Vec::new();
let dir_path = "/applications/";

match fs::open_via_fs(dir_path) {
Ok(fd) => {
let mut buf = vec![0u8; 16 * 1024];
let n = fs::readdir(fd, &mut buf);

fs::close_via_fs(fd);

if n == 0 {
return apps;
}

if n > buf.len() as u64 {
if n == 0 || n > buf.len() as u64 {
return apps;
}

let text = match core::str::from_utf8(&buf[..n as usize]) {
Ok(t) => t,
Err(_) => return apps,
};

for line in text.lines() {
let name_str = line.trim();
if name_str.is_empty() || !name_str.ends_with(".app") {
continue;
}

let app_path = format!("{}{}", dir_path, name_str);
let about_toml_path = format!("{}/about.toml", app_path);
let icon = read_icon_from_about_toml(&about_toml_path);
Expand All @@ -167,7 +165,7 @@ fn list_app_bundles() -> Vec<(String, Option<String>)> {
}
Err(_) => {}
}

apps.sort_by(|a, b| a.0.cmp(&b.0));
apps
}
Expand All @@ -183,6 +181,7 @@ fn read_icon_from_about_toml(path: &str) -> Option<String> {
let value = value
.trim()
.trim_matches(|c| c == '"' || c == '\'' || c == ' ');

if !value.is_empty() {
if value.starts_with('/') {
return Some(value.to_string());
Expand Down