diff --git a/Cargo.lock b/Cargo.lock
index cdfab1d4..9c9496da 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -18,6 +18,7 @@ dependencies = [
"truck-meshalgo",
"truck-modeling",
"truck-polymesh",
+ "windows-sys 0.59.0",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 31433981..abe64f6d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,3 +17,6 @@ open = "5"
printpdf = "0.9.1"
flate2 = "1"
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
+
+[target.'cfg(target_os = "windows")'.dependencies]
+windows-sys = { version = "0.59", features = ["Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] }
diff --git a/assets/icons/about.svg b/assets/icons/about.svg
new file mode 100644
index 00000000..7c3b9c03
--- /dev/null
+++ b/assets/icons/about.svg
@@ -0,0 +1,8 @@
+
diff --git a/assets/icons/changelog.svg b/assets/icons/changelog.svg
new file mode 100644
index 00000000..0779b636
--- /dev/null
+++ b/assets/icons/changelog.svg
@@ -0,0 +1,9 @@
+
diff --git a/assets/icons/report.svg b/assets/icons/report.svg
new file mode 100644
index 00000000..b51e1b25
--- /dev/null
+++ b/assets/icons/report.svg
@@ -0,0 +1,17 @@
+
diff --git a/src/app/commands.rs b/src/app/commands.rs
index 2e8ce892..1f3c26d6 100644
--- a/src/app/commands.rs
+++ b/src/app/commands.rs
@@ -2042,6 +2042,20 @@ impl H7CAD {
self.command_line.push_info("Opening Patreon page...");
}
+ "REPORT" => {
+ let _ = open::that("https://github.com/HakanSeven12/H7CAD/issues/new");
+ self.command_line.push_info("Opening GitHub issue page...");
+ }
+
+ "ABOUT" => {
+ return Task::done(Message::AboutOpen);
+ }
+
+ "CHANGELOG" => {
+ let _ = open::that("https://github.com/HakanSeven12/H7CAD/releases");
+ self.command_line.push_info("Opening release notes...");
+ }
+
// ── Keyboard Shortcuts panel ──────────────────────────────────
cmd if cmd == "SHORTCUTS" || cmd.starts_with("SHORTCUTS ") => {
let raw_rest = cmd.trim_start_matches("SHORTCUTS").trim();
diff --git a/src/app/mod.rs b/src/app/mod.rs
index 0f66ca57..db6f9e9c 100644
--- a/src/app/mod.rs
+++ b/src/app/mod.rs
@@ -68,6 +68,7 @@ pub(super) struct H7CAD {
plotstyle_window: Option,
dimstyle_window: Option,
shortcuts_window: Option,
+ about_window: Option,
/// In-memory clipboard: cloned entities waiting to be pasted.
clipboard: Vec,
/// Centroid of the clipboard entities (XZ plane, Y-up).
@@ -372,6 +373,9 @@ pub enum Message {
ShortcutsPanelOpen,
#[allow(dead_code)]
ShortcutsPanelClose,
+ // ── About window ────────────────────────────────────────────────────
+ AboutOpen,
+ AboutCopyInfo,
/// Close the viewport right-click context menu without performing any action.
ViewportContextMenuClose,
/// A window was closed by the OS (e.g. the user clicked the title-bar ✕).
@@ -552,6 +556,7 @@ impl H7CAD {
plotstyle_window: None,
dimstyle_window: None,
shortcuts_window: None,
+ about_window: None,
clipboard: Vec::new(),
clipboard_centroid: glam::Vec3::ZERO,
layout_context_menu: None,
@@ -644,6 +649,7 @@ pub fn run() -> iced::Result {
if Some(window_id) == state.plotstyle_window { return "Plot Style Table Editor".into(); }
if Some(window_id) == state.dimstyle_window { return "Dimension Style Manager".into(); }
if Some(window_id) == state.shortcuts_window { return "Keyboard Shortcuts".into(); }
+ if Some(window_id) == state.about_window { return "About H7CAD".into(); }
if let Some(tab) = state.tabs.get(state.active_tab) {
let dot = if tab.dirty { "● " } else { "" };
let name = tab.tab_display_name();
diff --git a/src/app/update.rs b/src/app/update.rs
index fafd66fb..ba72e71e 100644
--- a/src/app/update.rs
+++ b/src/app/update.rs
@@ -704,6 +704,7 @@ impl H7CAD {
if self.plotstyle_window == Some(id) { self.plotstyle_window = None; }
if self.dimstyle_window == Some(id) { self.dimstyle_window = None; }
if self.shortcuts_window == Some(id) { self.shortcuts_window = None; }
+ if self.about_window == Some(id) { self.about_window = None; }
Task::none()
}
@@ -2502,6 +2503,30 @@ impl H7CAD {
}
}
+ // ── About window ──────────────────────────────────────────────
+ Message::AboutOpen => {
+ if let Some(id) = self.about_window {
+ return window::gain_focus(id);
+ }
+ let (id, task) = window::open(window::Settings {
+ size: iced::Size::new(340.0, 240.0),
+ resizable: false,
+ ..Default::default()
+ });
+ self.about_window = Some(id);
+ task.map(|_| Message::Noop)
+ }
+
+ Message::AboutCopyInfo => {
+ let info = format!(
+ "H7CAD v{}\nOS: {}\nArch: {}",
+ env!("CARGO_PKG_VERSION"),
+ std::env::consts::OS,
+ std::env::consts::ARCH,
+ );
+ iced::clipboard::write(info)
+ }
+
Message::ViewportContextMenuClose => {
let i = self.active_tab;
self.tabs[i].scene.selection.borrow_mut().context_menu = None;
diff --git a/src/app/view.rs b/src/app/view.rs
index 3670b5c5..20341551 100644
--- a/src/app/view.rs
+++ b/src/app/view.rs
@@ -96,6 +96,9 @@ impl H7CAD {
if Some(window_id) == self.shortcuts_window {
return crate::ui::shortcuts::view_window(&self.shortcut_overrides);
}
+ if Some(window_id) == self.about_window {
+ return crate::ui::about::view_window();
+ }
let i = self.active_tab;
let tab = &self.tabs[i];
diff --git a/src/modules/home/about.rs b/src/modules/home/about.rs
new file mode 100644
index 00000000..854cf2f5
--- /dev/null
+++ b/src/modules/home/about.rs
@@ -0,0 +1,10 @@
+use crate::modules::{IconKind, ModuleEvent, ToolDef};
+
+pub fn tool() -> ToolDef {
+ ToolDef {
+ id: "ABOUT",
+ label: "About",
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/about.svg")),
+ event: ModuleEvent::Command("ABOUT".to_string()),
+ }
+}
diff --git a/src/modules/home/changelog.rs b/src/modules/home/changelog.rs
new file mode 100644
index 00000000..8987560f
--- /dev/null
+++ b/src/modules/home/changelog.rs
@@ -0,0 +1,10 @@
+use crate::modules::{IconKind, ModuleEvent, ToolDef};
+
+pub fn tool() -> ToolDef {
+ ToolDef {
+ id: "CHANGELOG",
+ label: "Changelog",
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/changelog.svg")),
+ event: ModuleEvent::Command("CHANGELOG".to_string()),
+ }
+}
diff --git a/src/modules/home/mod.rs b/src/modules/home/mod.rs
index 57d23631..9e3bda4b 100644
--- a/src/modules/home/mod.rs
+++ b/src/modules/home/mod.rs
@@ -2,8 +2,11 @@
pub mod clipboard;
pub mod defaults;
+mod about;
+mod changelog;
mod donate;
pub mod draw;
+mod report;
pub mod groups;
pub mod inquiry;
pub mod layers;
@@ -27,7 +30,10 @@ impl CadModule for HomeModule {
use crate::modules::annotate::{angular_dim, leader_cmd, linear_dim, mleader_cmd, mtext, radius_dim, text};
use crate::modules::insert::{create_block, insert_block};
use clipboard::{copy_clip, cut, paste};
+ use about;
+ use changelog;
use donate;
+ use report;
use draw::{arc, circle, ellipse, hatch, line, polyline, shapes};
use layers::{layfrz, laylck, layoff, layon, laythw, layulk, make_current, match_layer, panel};
use groups::{group, ungroup};
@@ -187,6 +193,9 @@ impl CadModule for HomeModule {
title: "Support",
tools: vec![
RibbonItem::LargeTool(donate::tool()),
+ report::tool().into(),
+ about::tool().into(),
+ changelog::tool().into(),
],
},
]
diff --git a/src/modules/home/report.rs b/src/modules/home/report.rs
new file mode 100644
index 00000000..48424c30
--- /dev/null
+++ b/src/modules/home/report.rs
@@ -0,0 +1,10 @@
+use crate::modules::{IconKind, ModuleEvent, ToolDef};
+
+pub fn tool() -> ToolDef {
+ ToolDef {
+ id: "REPORT",
+ label: "Report",
+ icon: IconKind::Svg(include_bytes!("../../../assets/icons/report.svg")),
+ event: ModuleEvent::Command("REPORT".to_string()),
+ }
+}
diff --git a/src/ui/about.rs b/src/ui/about.rs
new file mode 100644
index 00000000..a349d65a
--- /dev/null
+++ b/src/ui/about.rs
@@ -0,0 +1,96 @@
+use crate::app::Message;
+use iced::widget::{button, column, container, row, text, Space};
+use iced::{Background, Border, Color, Element, Fill, Theme};
+
+const BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
+const PANEL: Color = Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 };
+const BORDER: Color = Color { r: 0.30, g: 0.30, b: 0.30, a: 1.0 };
+const DIM: Color = Color { r: 0.55, g: 0.55, b: 0.55, a: 1.0 };
+const ACCENT: Color = Color { r: 0.25, g: 0.50, b: 0.85, a: 1.0 };
+const WHITE: Color = Color { r: 0.92, g: 0.92, b: 0.92, a: 1.0 };
+
+fn info_row<'a>(label: &'static str, value: String) -> Element<'a, Message> {
+ row![
+ text(label).size(11).color(DIM).width(100),
+ text(value).size(11).color(WHITE),
+ ]
+ .spacing(8)
+ .align_y(iced::Center)
+ .padding([3, 0])
+ .into()
+}
+
+pub fn view_window<'a>() -> Element<'a, Message> {
+ let version = env!("CARGO_PKG_VERSION");
+ let os = std::env::consts::OS;
+ let arch = std::env::consts::ARCH;
+
+ let logo = container(
+ column![
+ text("H7CAD").size(32).color(ACCENT),
+ text("CAD application for Architecture & Engineering")
+ .size(11).color(DIM),
+ ]
+ .spacing(4)
+ .align_x(iced::Center),
+ )
+ .width(Fill)
+ .padding(iced::Padding { top: 20.0, right: 0.0, bottom: 16.0, left: 0.0 })
+ .align_x(iced::Center);
+
+ let info_block = container(
+ column![
+ info_row("Version", format!("v{}", version)),
+ info_row("Platform", os.to_string()),
+ info_row("Arch", arch.to_string()),
+ ]
+ .spacing(2)
+ .padding([12, 16]),
+ )
+ .width(Fill)
+ .style(|_: &Theme| container::Style {
+ background: Some(Background::Color(PANEL)),
+ border: Border {
+ color: BORDER,
+ width: 1.0,
+ radius: 4.0.into(),
+ },
+ ..Default::default()
+ });
+
+ let copy_btn = button(
+ text("Copy Info").size(11),
+ )
+ .on_press(Message::AboutCopyInfo)
+ .style(|_: &Theme, st| button::Style {
+ background: Some(Background::Color(match st {
+ button::Status::Hovered | button::Status::Pressed =>
+ Color { r: 0.20, g: 0.42, b: 0.72, a: 1.0 },
+ _ => ACCENT,
+ })),
+ text_color: WHITE,
+ border: Border { radius: 4.0.into(), ..Default::default() },
+ ..Default::default()
+ })
+ .padding([6, 16]);
+
+ let footer = row![
+ Space::new().width(Fill),
+ copy_btn,
+ ]
+ .align_y(iced::Center)
+ .padding(iced::Padding { top: 12.0, right: 0.0, bottom: 0.0, left: 0.0 });
+
+ container(
+ column![logo, info_block, footer]
+ .spacing(0)
+ .padding(iced::Padding { top: 0.0, right: 20.0, bottom: 20.0, left: 20.0 }),
+ )
+ .style(|_: &Theme| container::Style {
+ background: Some(Background::Color(BG)),
+ ..Default::default()
+ })
+ .width(Fill)
+ .height(Fill)
+ .into()
+}
diff --git a/src/ui/mod.rs b/src/ui/mod.rs
index 7806c924..8d823f69 100644
--- a/src/ui/mod.rs
+++ b/src/ui/mod.rs
@@ -2,6 +2,7 @@
/// Change this to scale the ribbon, layer manager rows, and property panel rows uniformly.
pub const ROW_H: f32 = 26.0;
+pub mod about;
pub mod app_menu;
pub mod command_line;
pub mod dimstyle;