Files
oak-editor/src/oakui/icons.rs
T
Mike-Solar 18ff60f147 feat(engine): clip move, clip effect_input, mandatory static FFmpeg
- oakengine_sequence_move_clip implemented for real (oaktimeline
  TrackMoveBlockCommand; fixes the graph-ownership/gap-anchor/ripple
  trim bugs the stub was hiding); same-track via the frozen C ABI,
  cross-track supported by the module command
- oaknode clip blocks now declare a tex_in texture input and set
  effect_input to it, so timeline clips can host effect chains; facade
  test covers effect insert/remove on a real clip
- oakffmpeg-link: FFMPEG_DIR is now mandatory with a clear panic (a
  Homebrew upgrade left the system ffmpeg .pc pointing at a deleted
  dav1d Cellar path, breaking links); reads a git-ignored workspace
  .env for IDEs that cannot inject env vars (RustRover); links the C++
  stdlib for C++ codec libs (svt-av1)
- oakengine re-exports oaknode so tests share one crate instance;
  it_node uses the direct instance's value type where it calls the
  module FFI (the --workspace dev-dependency feature split builds
  oaknode twice)
2026-08-11 23:04:48 +08:00

138 lines
4.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! Theme-aware toolbar icons.
//!
//! The toolbar/transport icons are the PNGs pulled from the legacy C++ app
//! (`app/ui/style/olive-{dark,light}/png` in the pre-Rust history), stored in
//! `assets/icons/{dark,light}/`. Each theme ships its own glyph color (white
//! on dark, black on light), so the active [`OakTheme`] picks the family.
//!
//! Icons render on a 16px logical grid from the 32px (2×) files; buttons give
//! them a 24px hit target and a localized tooltip.
//!
//! [`OakTheme`]: gpui_widgets::theme::OakTheme
use std::path::PathBuf;
use gpui::App;
/// Icon file names (without the `.png` suffix), mirroring the legacy set.
pub const ICON_ARROW: &str = "arrow";
pub const ICON_RAZOR: &str = "razor";
pub const ICON_RIPPLE: &str = "ripple";
pub const ICON_SLIP: &str = "slip";
pub const ICON_ROLLING: &str = "rolling";
pub const ICON_ZOOM: &str = "zoomin";
pub const ICON_ZOOM_IN: &str = "zoomin";
pub const ICON_ZOOM_OUT: &str = "zoomout";
pub const ICON_SLIDE: &str = "slide";
pub const ICON_TRACK_SELECT: &str = "track-tool";
pub const ICON_SNAP: &str = "magnet";
pub const ICON_PLAY: &str = "play";
pub const ICON_PAUSE: &str = "pause";
pub const ICON_PREV: &str = "prev";
pub const ICON_NEXT: &str = "next";
pub const ICON_REW: &str = "rew";
pub const ICON_FF: &str = "ff";
/// The theme-dependent filesystem path of an icon (`assets/icons/{dark,light}`
/// under the crate root). Absolute, so it works from any working directory.
pub fn icon_path(name: &str, cx: &App) -> PathBuf {
let theme = gpui_widgets::theme::current_theme(cx);
let family = if theme.name == "Olive Dark" {
"dark"
} else {
"light"
};
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("assets/icons")
.join(family)
.join(format!("{name}.png"))
}
/// Registers the app's icon resolver, so widget-crate consumers (the viewer
/// transport bar) resolve the same theme-aware paths. Call at startup before
/// any window renders; re-registering after a theme switch is harmless.
pub fn init(cx: &mut App) {
gpui_widgets::icons::set_resolver(
std::sync::Arc::new(|name, cx| Some(icon_path(name, cx))),
cx,
);
}
#[cfg(test)]
mod tests {
use super::*;
/// Every icon name resolves to an existing file for both themes — a
/// missing PNG would render as a blank toolbar button.
#[test]
fn every_icon_file_exists_for_both_themes() {
for name in [
ICON_ARROW,
ICON_RAZOR,
ICON_RIPPLE,
ICON_SLIP,
ICON_ROLLING,
ICON_ZOOM,
ICON_ZOOM_IN,
ICON_ZOOM_OUT,
ICON_SLIDE,
ICON_TRACK_SELECT,
ICON_SNAP,
ICON_PLAY,
ICON_PAUSE,
ICON_PREV,
ICON_NEXT,
ICON_REW,
ICON_FF,
] {
for family in ["dark", "light"] {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("assets/icons")
.join(family)
.join(format!("{name}.png"));
assert!(
path.exists(),
"missing icon asset {path:?} (referenced as {name})"
);
}
}
}
/// The theme-aware path picks the dark family for the default dark theme
/// and the light family once a light theme is applied.
#[gpui::test]
fn icon_path_follows_the_active_theme(cx: &mut gpui::TestAppContext) {
cx.update(|app| {
gpui_widgets::theme::apply_theme(app, &gpui_widgets::theme::OakTheme::olive_dark());
let dark = icon_path(ICON_PLAY, app);
assert!(
dark.ends_with("dark/play.png"),
"dark theme → dark family, got {dark:?}"
);
gpui_widgets::theme::apply_theme(app, &gpui_widgets::theme::OakTheme::olive_light());
let light = icon_path(ICON_PLAY, app);
assert!(
light.ends_with("light/play.png"),
"light theme → light family, got {light:?}"
);
});
}
}