initial qt gui test
parent
56df373dc1
commit
d9ca7a3866
|
@ -0,0 +1,47 @@
|
|||
cmake_minimum_required(VERSION 3.14)
|
||||
project(maraiah-tycho CXX)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
set(RS_DIR "${CMAKE_BINARY_DIR}/maraiah-tycho-lib")
|
||||
set(RS_OUT "${RS_DIR}/build")
|
||||
set(RS_LIB "${RS_OUT}/debug/libtycho.a")
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(Qt5Widgets CONFIG REQUIRED)
|
||||
|
||||
add_executable(
|
||||
maraiah-tycho
|
||||
WIN32
|
||||
source/tycho-qt/main.cc
|
||||
source/tycho-qt/mainwindow.cc
|
||||
source/tycho-qt/mainwindow.h
|
||||
source/tycho-qt/mainwindow.ui
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
maraiah-tycho-lib ALL
|
||||
BYPRODUCTS "${RS_LIB}"
|
||||
COMMAND cargo build -p maraiah-tycho --target-dir "${RS_OUT}"
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT "Building Rust library"
|
||||
)
|
||||
|
||||
set_target_properties(
|
||||
maraiah-tycho
|
||||
PROPERTIES
|
||||
CXX_STANDARD 17
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
maraiah-tycho
|
||||
"${RS_LIB}"
|
||||
Threads::Threads
|
||||
${CMAKE_DL_LIBS}
|
||||
Qt5::Widgets
|
||||
)
|
||||
|
||||
## EOF
|
|
@ -0,0 +1,18 @@
|
|||
#include "mainwindow.h"
|
||||
#include <QApplication>
|
||||
#include <cstdio>
|
||||
|
||||
extern "C" void test_fn();
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
puts("hello, world");
|
||||
test_fn();
|
||||
|
||||
QApplication a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -0,0 +1,16 @@
|
|||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#ifndef tycho_qt__mainwindow_h
|
||||
#define tycho_qt__mainwindow_h
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
}
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QMenuBar" name="menuBar" />
|
||||
<widget class="QToolBar" name="mainToolBar" />
|
||||
<widget class="QWidget" name="centralWidget" />
|
||||
<widget class="QStatusBar" name="statusBar" />
|
||||
</widget>
|
||||
<layoutDefault spacing="6" margin="11" />
|
||||
<pixmapfunction></pixmapfunction>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -4,22 +4,12 @@ version = "0.1.0"
|
|||
authors = ["Alison Sanderson <marrub@greyserv.net>"]
|
||||
description = "Tycho map editor."
|
||||
edition = "2018"
|
||||
build = "build.rs"
|
||||
|
||||
[dependencies]
|
||||
atk-sys = "0.8"
|
||||
cairo-sys-rs = "0.8"
|
||||
gdk-pixbuf-sys = "0.8"
|
||||
gdk-sys = "0.8"
|
||||
gio-sys = "0.8"
|
||||
glib-sys = "0.8"
|
||||
gobject-sys = "0.8"
|
||||
gtk-sys = {version = "0.8", features = ["v3_16"]}
|
||||
maraiah = {path = "../.."}
|
||||
memmap = "0.7"
|
||||
pango-sys = "0.8"
|
||||
pangocairo-sys = "0.9"
|
||||
maraiah = {path = "../.."}
|
||||
memmap = "0.7"
|
||||
|
||||
[[bin]]
|
||||
[lib]
|
||||
name = "tycho"
|
||||
path = "main.rs"
|
||||
path = "lib.rs"
|
||||
crate-type = ["staticlib"]
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
use std::{env, fs, io, path::Path, process::Command};
|
||||
|
||||
fn io_err(st: &'static str) -> io::Error
|
||||
{
|
||||
io::Error::new(io::ErrorKind::Other, st)
|
||||
}
|
||||
|
||||
fn traverse_dir(path: &Path) -> io::Result<()>
|
||||
{
|
||||
for ent in fs::read_dir(path)? {
|
||||
let path = ent?.path();
|
||||
|
||||
if path.is_dir() {
|
||||
traverse_dir(&path)?;
|
||||
} else {
|
||||
if let Some(path) = path.to_str() {
|
||||
println!("cargo:rerun-if-changed={}", path);
|
||||
} else {
|
||||
return Err(io_err("failed to convert path"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> io::Result<()>
|
||||
{
|
||||
let out_file = env::var("OUT_DIR").unwrap();
|
||||
let out_file = format!("--target={}/resources", out_file);
|
||||
|
||||
// traverse each file in the data directory, because cargo won't do this
|
||||
traverse_dir(Path::new("data"))?;
|
||||
|
||||
let o = Command::new("glib-compile-resources").arg("data/resources.xml")
|
||||
.arg(out_file)
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
if !o.status.success() {
|
||||
dbg!(o);
|
||||
Err(io_err("failed to compile resources"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<!-- Toplevel namespace. Contains UI and helpers. -->
|
||||
<gresource prefix="/net/greyserv/maraiah/tycho">
|
||||
<file compressed="true" alias="tycho1.png">data/misc/tycho1.png</file>
|
||||
<file compressed="true" alias="tycho2.png">data/misc/tycho2.png</file>
|
||||
<file compressed="true" alias="css">data/styles.css</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks" alias="ui">data/ui.xml</file>
|
||||
</gresource>
|
||||
<!-- Icons, color. We can't make highcolor yet. -->
|
||||
<gresource prefix="/net/greyserv/maraiah/tycho/icons/48x48/actions">
|
||||
<file compressed="true" alias="tycho-polys.png">data/color/polygons.png</file>
|
||||
<file compressed="true" alias="tycho-lines.png">data/color/lines.png</file>
|
||||
<file compressed="true" alias="tycho-points.png">data/color/points.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/net/greyserv/maraiah/tycho/icons/48x48/apps">
|
||||
<file compressed="true" alias="tycho-map.png">data/color/map.png</file>
|
||||
</gresource>
|
||||
</gresources>
|
|
@ -1 +0,0 @@
|
|||
/* EOF */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,9 +0,0 @@
|
|||
//! Interfaces to the system.
|
||||
|
||||
pub mod cairo;
|
||||
pub mod editor;
|
||||
pub mod glib;
|
||||
|
||||
pub use self::{cairo::*, editor::*, glib::*};
|
||||
|
||||
// EOF
|
|
@ -1,156 +0,0 @@
|
|||
//! Implemented drawing area for Cairo.
|
||||
|
||||
use super::glib::*;
|
||||
use cairo_sys::*;
|
||||
use gdk_pixbuf_sys::*;
|
||||
use gdk_sys::*;
|
||||
use pango_sys::*;
|
||||
use pango_cairo_sys::*;
|
||||
use maraiah::{c_str,
|
||||
durandal::{ffi, image::*},
|
||||
rozinante::draw};
|
||||
|
||||
/// Converts a `Color` to a `f64` triple.
|
||||
fn flt_color(cr: impl Color) -> (f64, f64, f64)
|
||||
{
|
||||
fn flt_color(n: u16) -> f64 {f64::from(n) / f64::from(u16::max_value())}
|
||||
|
||||
(flt_color(cr.r()), flt_color(cr.g()), flt_color(cr.b()))
|
||||
}
|
||||
|
||||
impl draw::CacheImage for CrImage
|
||||
{
|
||||
fn w(&self) -> draw::Coord
|
||||
{
|
||||
unsafe {gdk_pixbuf_get_width(self.0) as draw::Coord}
|
||||
}
|
||||
|
||||
fn h(&self) -> draw::Coord
|
||||
{
|
||||
unsafe {gdk_pixbuf_get_height(self.0) as draw::Coord}
|
||||
}
|
||||
}
|
||||
|
||||
impl CrDrawArea
|
||||
{
|
||||
/// Creates a new `CrDrawArea`.
|
||||
pub fn new(ctx: *mut cairo_t, w: f64, h: f64) -> Self
|
||||
{
|
||||
let pan = unsafe {
|
||||
let pan = pango_cairo_create_layout(ctx);
|
||||
let dsc = pango_font_description_from_string(c_str!("Monospace 12"));
|
||||
pango_layout_set_font_description(pan, dsc);
|
||||
pango_font_description_free(dsc);
|
||||
pan
|
||||
};
|
||||
|
||||
let pan = Refc::new(pan);
|
||||
|
||||
CrDrawArea{ctx, pan, w: w as draw::Coord, h: h as draw::Coord}
|
||||
}
|
||||
}
|
||||
|
||||
impl draw::DrawArea for CrDrawArea
|
||||
{
|
||||
type NativeImage = CrImage;
|
||||
|
||||
fn w(&self) -> draw::Coord {self.w}
|
||||
fn h(&self) -> draw::Coord {self.h}
|
||||
|
||||
fn clear(&mut self, cr: impl Color)
|
||||
{
|
||||
self.rect(draw::Rect{x: 0, y: 0, w: self.w(), h: self.h()}, cr);
|
||||
|
||||
unsafe {
|
||||
cairo_set_line_width(self.ctx, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn line_width(&mut self, width: u8)
|
||||
{
|
||||
let width = f64::from(width);
|
||||
|
||||
unsafe {
|
||||
cairo_set_line_width(self.ctx, width);
|
||||
}
|
||||
}
|
||||
|
||||
fn line(&mut self, p1: draw::Point, p2: draw::Point, cr: impl Color)
|
||||
{
|
||||
let (r, g, b) = flt_color(cr);
|
||||
|
||||
let x1 = f64::from(p1.0);
|
||||
let y1 = f64::from(p1.1);
|
||||
|
||||
let x2 = f64::from(p2.0);
|
||||
let y2 = f64::from(p2.1);
|
||||
|
||||
unsafe {
|
||||
cairo_set_source_rgb(self.ctx, r, g, b);
|
||||
cairo_move_to(self.ctx, x1, y1);
|
||||
cairo_line_to(self.ctx, x2, y2);
|
||||
cairo_stroke(self.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn rect(&mut self, rect: draw::Rect, cr: impl Color)
|
||||
{
|
||||
let px = f64::from(rect.x);
|
||||
let py = f64::from(rect.y);
|
||||
let sx = f64::from(rect.w);
|
||||
let sy = f64::from(rect.h);
|
||||
|
||||
let (r, g, b) = flt_color(cr);
|
||||
|
||||
unsafe {
|
||||
cairo_set_source_rgb(self.ctx, r, g, b);
|
||||
cairo_rectangle(self.ctx, px, py, sx, sy);
|
||||
cairo_fill(self.ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn text(&mut self, pos: draw::Point, text: &str, cr: impl Color)
|
||||
{
|
||||
let (r, g, b) = flt_color(cr);
|
||||
|
||||
let x = f64::from(pos.0);
|
||||
let y = f64::from(pos.1);
|
||||
|
||||
let tlen = text.len() as ffi::c_int;
|
||||
let text = text.as_ptr() as ffi::NT;
|
||||
|
||||
unsafe {
|
||||
cairo_set_source_rgb(self.ctx, r, g, b);
|
||||
cairo_move_to(self.ctx, x, y);
|
||||
|
||||
pango_layout_set_markup(*self.pan, text, tlen);
|
||||
pango_cairo_update_layout(self.ctx, *self.pan);
|
||||
pango_cairo_show_layout(self.ctx, *self.pan);
|
||||
}
|
||||
}
|
||||
|
||||
fn image(&mut self, pos: draw::Point, im: &Self::NativeImage)
|
||||
{
|
||||
let x = f64::from(pos.0);
|
||||
let y = f64::from(pos.1);
|
||||
|
||||
unsafe {
|
||||
gdk_cairo_set_source_pixbuf(self.ctx, im.0, x, y);
|
||||
cairo_paint(self.ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An image for a `CrDrawArea`.
|
||||
pub struct CrImage(pub *const GdkPixbuf);
|
||||
|
||||
/// A `DrawArea` for a Cairo surface.
|
||||
pub struct CrDrawArea
|
||||
{
|
||||
ctx: *mut cairo_t,
|
||||
pan: Refc<'static, PangoLayout>,
|
||||
w: draw::Coord,
|
||||
h: draw::Coord,
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -1,49 +0,0 @@
|
|||
//! Map editor interface.
|
||||
|
||||
use super::glib::*;
|
||||
use gobject_sys::*;
|
||||
use gtk_sys::*;
|
||||
use maraiah::{durandal::ffi, marathon::map};
|
||||
use std::cell::RefCell;
|
||||
|
||||
impl EditorModel
|
||||
{
|
||||
pub fn new(view: EditorView) -> Self
|
||||
{
|
||||
Self{view}
|
||||
}
|
||||
|
||||
pub fn open_new(&mut self)
|
||||
{
|
||||
eprintln!("open_new");
|
||||
}
|
||||
|
||||
pub fn open_buf(&mut self, b: &[u8])
|
||||
{
|
||||
eprintln!("open_buf");
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EditorPresenter
|
||||
{
|
||||
}
|
||||
|
||||
pub struct EditorView
|
||||
{
|
||||
pub prop: PropertiesWindow,
|
||||
}
|
||||
|
||||
pub struct PropertiesWindow
|
||||
{
|
||||
pub flg_ent: Vec<Refc<'static, GtkToggleButton>>,
|
||||
pub flg_env: Vec<Refc<'static, GtkToggleButton>>,
|
||||
pub flg_msn: Vec<Refc<'static, GtkToggleButton>>,
|
||||
}
|
||||
|
||||
pub struct EditorModel
|
||||
{
|
||||
}
|
||||
|
||||
pub type EditorRef = std::cell::RefCell<EditorModel>;
|
||||
|
||||
// EOF
|
|
@ -1,113 +0,0 @@
|
|||
//! GLib interfaces.
|
||||
|
||||
use glib_sys::*;
|
||||
use gobject_sys::*;
|
||||
use maraiah::durandal::ffi;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
impl<T> std::ops::Deref for Refc<'_, T>
|
||||
{
|
||||
type Target = *mut T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {&self.p}
|
||||
}
|
||||
|
||||
impl<T> Drop for Refc<'_, T>
|
||||
{
|
||||
fn drop(&mut self)
|
||||
{
|
||||
unsafe {
|
||||
g_object_unref(self.p as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Refc<'_, T>
|
||||
{
|
||||
/// Creates a new `Refc` which will own an already referenced object.
|
||||
pub const fn new(p: *mut T) -> Self {Self{p, l: PhantomData}}
|
||||
|
||||
/// Creates a new `Refc` which will hold a reference to the object.
|
||||
pub fn own(p: *mut T) -> Self {unsafe {g_object_ref(p as _);} Self::new(p)}
|
||||
}
|
||||
|
||||
impl ListD
|
||||
{
|
||||
pub fn new(head: *mut GList) -> Self {Self{head, iter: head}}
|
||||
}
|
||||
|
||||
impl Iterator for ListD
|
||||
{
|
||||
type Item = gpointer;
|
||||
|
||||
fn next(&mut self) -> Option<gpointer> {
|
||||
if self.iter != ffi::null_mut() {
|
||||
let obj = unsafe {
|
||||
let obj = (*self.iter).data;
|
||||
self.iter = (*self.iter).next;
|
||||
obj
|
||||
};
|
||||
|
||||
Some(obj)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ListD
|
||||
{
|
||||
fn drop(&mut self) {unsafe {g_list_free(self.head);}}
|
||||
}
|
||||
|
||||
impl ListS
|
||||
{
|
||||
pub fn new(head: *mut GSList) -> Self {Self{head, iter: head}}
|
||||
}
|
||||
|
||||
impl Iterator for ListS
|
||||
{
|
||||
type Item = gpointer;
|
||||
|
||||
fn next(&mut self) -> Option<gpointer> {
|
||||
if self.iter != ffi::null_mut() {
|
||||
let obj = unsafe {
|
||||
let obj = (*self.iter).data;
|
||||
self.iter = (*self.iter).next;
|
||||
obj
|
||||
};
|
||||
|
||||
Some(obj)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ListS
|
||||
{
|
||||
fn drop(&mut self) {unsafe {g_slist_free(self.head);}}
|
||||
}
|
||||
|
||||
/// A GObject owned pointer.
|
||||
pub struct Refc<'a, T>
|
||||
{
|
||||
p: *mut T,
|
||||
l: PhantomData<&'a *mut T>,
|
||||
}
|
||||
|
||||
/// A GList wrapper.
|
||||
pub struct ListD
|
||||
{
|
||||
head: *mut GList,
|
||||
iter: *mut GList,
|
||||
}
|
||||
|
||||
/// A GSList wrapper.
|
||||
pub struct ListS
|
||||
{
|
||||
head: *mut GSList,
|
||||
iter: *mut GSList,
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -0,0 +1,7 @@
|
|||
#[no_mangle]
|
||||
pub extern "C" fn test_fn()
|
||||
{
|
||||
println!("hello, world! from rust");
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -1,374 +0,0 @@
|
|||
mod interfaces;
|
||||
|
||||
use self::interfaces::*;
|
||||
use gdk_pixbuf_sys::*;
|
||||
use gdk_sys::*;
|
||||
use gio_sys::*;
|
||||
use glib_sys::*;
|
||||
use gobject_sys::*;
|
||||
use gtk_sys::*;
|
||||
use maraiah::{c_str,
|
||||
durandal::{err::*, ffi}};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
// Called when the application activates in order to set everything up.
|
||||
unsafe extern "C" fn app_activate(app: *mut GtkApplication, _: gpointer)
|
||||
{
|
||||
// Callback to finalize the reference.
|
||||
unsafe extern "C" fn c_done(_: *mut GApplication, ptr: gpointer)
|
||||
{
|
||||
let edit = Rc::from_raw(ptr as *const EditorRef);
|
||||
assert_eq!(Rc::strong_count(&edit), 1);
|
||||
}
|
||||
|
||||
let b = Refc::new(gtk_builder_new_from_resource(PATH_BUILDER));
|
||||
|
||||
let prop = PropertiesWindow{flg_ent: get_flag_fields(&b, B_CON_F_ENT),
|
||||
flg_env: get_flag_fields(&b, B_CON_F_ENV),
|
||||
flg_msn: get_flag_fields(&b, B_CON_F_MSN)};
|
||||
|
||||
let edit = EditorModel::new(EditorView{prop});
|
||||
let edit = RefCell::new(edit);
|
||||
let edit = Rc::new(edit);
|
||||
|
||||
setup_css();
|
||||
//setup_draw_area(&b, edit.clone());
|
||||
let wv = setup_win(&b, B_WIN_M_VIEW, B_BTN_M_VIEW);
|
||||
let wt = setup_win(&b, B_WIN_M_TOOL, B_BTN_M_TOOL);
|
||||
let _ = setup_win(&b, B_WIN_M_PROP, B_BTN_M_PROP);
|
||||
gtk_widget_show_all(wv);
|
||||
gtk_widget_show_all(wt);
|
||||
|
||||
setup_about_dlg(&b);
|
||||
setup_win_main(&b, app, edit.clone());
|
||||
|
||||
connect(app, E_SHUTDOWN, c_done as _, Rc::into_raw(edit));
|
||||
}
|
||||
|
||||
// Gets all of the toggle buttons from a container.
|
||||
unsafe fn get_flag_fields(b: &Refc<GtkBuilder>, name: ffi::NT)
|
||||
-> Vec<Refc<'static, GtkToggleButton>>
|
||||
{
|
||||
let mut flags = Vec::new();
|
||||
|
||||
let head = get_obj(b, name);
|
||||
let head = ListD::new(gtk_container_get_children(head));
|
||||
let gtyp = gtk_toggle_button_get_type();
|
||||
|
||||
get_typed_from(head, gtyp, |obj| flags.push(Refc::own(obj as _)));
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
// Sets up the map view window.
|
||||
unsafe fn setup_win(b: &Refc<GtkBuilder>, win: ffi::NT, btn: ffi::NT)
|
||||
-> *mut GtkWidget
|
||||
{
|
||||
// Callback to show the widget.
|
||||
unsafe extern "C" fn c_show_act(_: *mut GtkWidget, wid: gpointer)
|
||||
{
|
||||
gtk_widget_show_all(wid as _);
|
||||
}
|
||||
|
||||
let win = get_obj::<GtkWindow >(b, win);
|
||||
let btn = get_obj::<GtkMenuItem>(b, btn);
|
||||
|
||||
connect_hide(win);
|
||||
connect(btn, E_ACTIVATE, c_show_act as _, win);
|
||||
|
||||
win as *mut GtkWidget
|
||||
}
|
||||
|
||||
// Sets up the about dialogue.
|
||||
unsafe fn setup_about_dlg(b: &Refc<GtkBuilder>)
|
||||
{
|
||||
// Callback to show the dialogue when the "About" button is pressed, and
|
||||
// hide it when the "Close" button is pressed on it.
|
||||
unsafe extern "C" fn c_show_act(_: *mut GtkWidget, dlg: gpointer)
|
||||
{
|
||||
gtk_dialog_run(dlg as _);
|
||||
gtk_widget_hide(dlg as _);
|
||||
}
|
||||
|
||||
let dlg = get_obj::<GtkAboutDialog>(b, B_DLG_ABOUT);
|
||||
let btn = get_obj::<GtkMenuItem >(b, B_BTN_ABOUT);
|
||||
|
||||
let it = env!("CARGO_PKG_AUTHORS").split(';');
|
||||
let mut v = ffi::CStringVec::new_from_iter(it).unwrap();
|
||||
let img = Refc::new(load_img(IM_ABOUT));
|
||||
|
||||
gtk_about_dialog_set_authors(dlg, v.as_mut_ptr());
|
||||
gtk_about_dialog_set_version(dlg, c_str!(env!("CARGO_PKG_VERSION")));
|
||||
gtk_about_dialog_set_website(dlg, c_str!(env!("CARGO_PKG_HOMEPAGE")));
|
||||
gtk_about_dialog_set_logo(dlg, *img);
|
||||
|
||||
connect_hide(dlg);
|
||||
connect(btn, E_ACTIVATE, c_show_act as _, dlg);
|
||||
}
|
||||
|
||||
// Sets up explicit window finalization for the main window.
|
||||
unsafe fn setup_explicit_drop(b: &Refc<GtkBuilder>, win: *mut GtkWindow)
|
||||
{
|
||||
// Callback to explicitly finalize all windows on exit.
|
||||
unsafe extern "C" fn c_done(_: *mut GtkWidget, exp_del: gpointer)
|
||||
{
|
||||
let exp_del = Box::from_raw(exp_del as *mut Vec<*mut GtkWindow>);
|
||||
|
||||
for win in *exp_del {
|
||||
gtk_widget_destroy(win as _);
|
||||
}
|
||||
}
|
||||
|
||||
// we need to explicitly drop other windows on exit, which means we need to
|
||||
// create a list of them to send to the callback
|
||||
let mut exp_del = Vec::new();
|
||||
|
||||
// so, we get all of the objects from the builder, and iterate through them
|
||||
let head = ListS::new(gtk_builder_get_objects(**b));
|
||||
|
||||
get_typed_from(head, gtk_window_get_type(), |obj| {
|
||||
let obj = obj as *mut GtkWindow;
|
||||
|
||||
if obj != win {
|
||||
exp_del.push(obj);
|
||||
}
|
||||
});
|
||||
|
||||
let exp_del = Box::into_raw(Box::new(exp_del));
|
||||
|
||||
connect(win, E_DESTROY, c_done as _, exp_del);
|
||||
}
|
||||
|
||||
// Get objects of type `ty` from `it`.
|
||||
unsafe fn get_typed_from<I, F>(it: I, ty: GType, mut f: F)
|
||||
where I: Iterator<Item = gpointer>,
|
||||
F: FnMut(*mut GObject)
|
||||
{
|
||||
for obj in it {
|
||||
// while this is well-defined, it is a weird way of doing it, because
|
||||
// this exact method of checking types isn't fully documented. we can't
|
||||
// use the macros for this functionality because we're not using C, so we
|
||||
// use the underlying function calls. again, get jacked, punk.
|
||||
if g_type_check_instance_is_a(obj as *mut GTypeInstance, ty) != 0 {
|
||||
f(obj as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sets up the main menu window.
|
||||
unsafe fn setup_win_main(b: &Refc<GtkBuilder>,
|
||||
app: *mut GtkApplication,
|
||||
edit: Rc<EditorRef>)
|
||||
{
|
||||
// Callback to close the window when the "Quit" button is pressed.
|
||||
unsafe extern "C" fn c_quit_act(_: *mut GtkWidget, win: gpointer)
|
||||
{
|
||||
gtk_window_close(win as _);
|
||||
}
|
||||
|
||||
// Callback to create a new map when the "New" button is pressed.
|
||||
unsafe extern "C" fn c_new_act(_: *mut GtkWidget, edit: gpointer)
|
||||
{
|
||||
let edit = &*(edit as *const EditorRef);
|
||||
|
||||
edit.borrow_mut().open_new();
|
||||
}
|
||||
|
||||
// Opens the map editor with a buffer.
|
||||
unsafe fn open_buf(path: &str, edit: &mut EditorModel) -> ResultS<()>
|
||||
{
|
||||
let fp = std::fs::File::open(&path)?;
|
||||
let mm = memmap::Mmap::map(&fp)?;
|
||||
|
||||
edit.open_buf(&mm);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Callback to open an existing map when the "Open" button is pressed.
|
||||
unsafe extern "C" fn c_open_act(_: *mut GtkWidget, edit: gpointer)
|
||||
{
|
||||
let edit = &*(edit as *const EditorRef);
|
||||
|
||||
if let Some(path) = run_file_chooser_open() {
|
||||
// TODO: handle errors gracefully
|
||||
open_buf(&path, &mut edit.borrow_mut()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// set up main window
|
||||
let win = get_obj(b, B_WIN_MAIN);
|
||||
|
||||
setup_explicit_drop(b, win);
|
||||
|
||||
gtk_window_set_application(win, app);
|
||||
gtk_widget_show_all(win as _);
|
||||
|
||||
// set up buttons
|
||||
let btn = get_obj::<GtkMenuItem>(b, B_BTN_QUIT);
|
||||
connect(btn, E_ACTIVATE, c_quit_act as _, win);
|
||||
|
||||
let btn = get_obj::<GtkMenuItem>(b, B_BTN_NEW);
|
||||
connect(btn, E_ACTIVATE, c_new_act as _, connect_ref(btn, edit.clone()));
|
||||
|
||||
let btn = get_obj::<GtkMenuItem>(b, B_BTN_OPEN);
|
||||
connect(btn, E_ACTIVATE, c_open_act as _, connect_ref(btn, edit.clone()));
|
||||
}
|
||||
|
||||
// Sets up the CSS styling providers.
|
||||
unsafe fn setup_css()
|
||||
{
|
||||
let css = Refc::new(gtk_css_provider_new());
|
||||
gtk_css_provider_load_from_resource(*css, PATH_CSS);
|
||||
|
||||
let scr = gdk_screen_get_default();
|
||||
let pri = GTK_STYLE_PROVIDER_PRIORITY_APPLICATION as u32;
|
||||
gtk_style_context_add_provider_for_screen(scr, *css as _, pri);
|
||||
}
|
||||
|
||||
// Runs a modal Open File dialogue.
|
||||
unsafe fn run_file_chooser_open() -> Option<String>
|
||||
{
|
||||
let action = GTK_FILE_CHOOSER_ACTION_OPEN;
|
||||
let dlg = gtk_file_chooser_dialog_new(c_str!("Open File"),
|
||||
ffi::null_mut(),
|
||||
action,
|
||||
c_str!("_Cancel"),
|
||||
GTK_RESPONSE_CANCEL,
|
||||
c_str!("_Open"),
|
||||
GTK_RESPONSE_ACCEPT,
|
||||
ffi::null_mut_void());
|
||||
|
||||
let res = gtk_dialog_run(dlg as _);
|
||||
|
||||
let ret = if res == GTK_RESPONSE_ACCEPT {
|
||||
let fna = gtk_file_chooser_get_filename(dlg as _);
|
||||
|
||||
let own = ffi::CStr::from_ptr(fna);
|
||||
let own = own.to_str().ok()?.to_owned();
|
||||
|
||||
g_free(fna as _);
|
||||
|
||||
Some(own)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
gtk_widget_destroy(dlg);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
// Connects a handler that hides a toplevel widget when deleted.
|
||||
unsafe fn connect_hide<T>(wid: *mut T)
|
||||
{
|
||||
// Callback to hide the widget.
|
||||
unsafe extern "C" fn c_hide_del(wid: *mut GtkWidget,
|
||||
_: *mut GdkEvent,
|
||||
_: gpointer)
|
||||
{
|
||||
gtk_widget_hide(wid);
|
||||
}
|
||||
|
||||
connect(wid, E_DELETE, c_hide_del as _, ffi::null_void());
|
||||
}
|
||||
|
||||
// Connects the map editor reference to a widget.
|
||||
unsafe fn connect_ref<T>(obj: *mut T, rc: Rc<EditorRef>) -> *const EditorRef
|
||||
{
|
||||
// Callback to finalize the reference.
|
||||
unsafe extern "C" fn c_done(_: *mut GtkWidget, ptr: gpointer)
|
||||
{
|
||||
Rc::from_raw(ptr as *const EditorRef);
|
||||
}
|
||||
|
||||
let ptr = Rc::into_raw(rc);
|
||||
|
||||
connect(obj, E_DESTROY, c_done as _, ptr);
|
||||
|
||||
ptr
|
||||
}
|
||||
|
||||
// Gets an object from a `GtkBuilder`.
|
||||
unsafe fn get_obj<T>(b: &Refc<GtkBuilder>, name: ffi::NT) -> *mut T
|
||||
{
|
||||
gtk_builder_get_object(**b, name) as _
|
||||
}
|
||||
|
||||
// Connects a signal handler.
|
||||
unsafe fn connect<T, U>(obj: *mut T, name: ffi::NT, cb: gpointer, d: *const U)
|
||||
-> ffi::c_ulong
|
||||
{
|
||||
let cb = std::mem::transmute(cb);
|
||||
g_signal_connect_data(obj as _, name, cb, d as _, None, 0)
|
||||
}
|
||||
|
||||
// Loads a `Pixbuf` from a resource.
|
||||
unsafe fn load_img(path: ffi::NT) -> *mut GdkPixbuf
|
||||
{
|
||||
gdk_pixbuf_new_from_resource(path, ffi::null_mut())
|
||||
}
|
||||
|
||||
// Entry point.
|
||||
fn main()
|
||||
{
|
||||
unsafe {
|
||||
// get jacked, punk. opaque data structures are for nerds.
|
||||
const RESOURCE_DATA: &[u8] =
|
||||
include_bytes!(concat!(env!("OUT_DIR"), "/resources"));
|
||||
|
||||
// first we create the static resource header, which is really simple
|
||||
let mut resource = GStaticResource{data: RESOURCE_DATA.as_ptr(),
|
||||
data_len: RESOURCE_DATA.len(),
|
||||
resource: ffi::null_mut(),
|
||||
next: ffi::null_mut(),
|
||||
padding: ffi::null_mut()};
|
||||
|
||||
// init it, now we can use it throughout the entire app without copying!
|
||||
g_static_resource_init(&mut resource);
|
||||
|
||||
{
|
||||
// create and run the app
|
||||
let app = Refc::new(gtk_application_new(APP_ID, 0));
|
||||
|
||||
connect(*app, E_ACTIVATE, app_activate as _, ffi::null_void());
|
||||
|
||||
g_application_run(*app as _, 0, ffi::null_mut());
|
||||
}
|
||||
|
||||
// deinit the "static" data, and everything will be done
|
||||
g_static_resource_fini(&mut resource);
|
||||
}
|
||||
}
|
||||
|
||||
const APP_ID: ffi::NT = c_str!("net.greyserv.maraiah.tycho");
|
||||
const B_ADJ_M_HORZ: ffi::NT = c_str!("adj-map-horz");
|
||||
const B_ADJ_M_VERT: ffi::NT = c_str!("adj-map-vert");
|
||||
const B_BTN_ABOUT: ffi::NT = c_str!("btn-about");
|
||||
const B_BTN_M_PROP: ffi::NT = c_str!("btn-show-map-prop");
|
||||
const B_BTN_M_TOOL: ffi::NT = c_str!("btn-show-map-tools");
|
||||
const B_BTN_M_VIEW: ffi::NT = c_str!("btn-show-map-view");
|
||||
const B_BTN_NEW: ffi::NT = c_str!("btn-new");
|
||||
const B_BTN_OPEN: ffi::NT = c_str!("btn-open");
|
||||
const B_BTN_QUIT: ffi::NT = c_str!("btn-quit");
|
||||
const B_CON_F_ENT: ffi::NT = c_str!("con-f-ent");
|
||||
const B_CON_F_ENV: ffi::NT = c_str!("con-f-env");
|
||||
const B_CON_F_MSN: ffi::NT = c_str!("con-f-msn");
|
||||
const B_DLG_ABOUT: ffi::NT = c_str!("dlg-about");
|
||||
const B_DRAW_AREA: ffi::NT = c_str!("draw-area");
|
||||
const B_WIN_MAIN: ffi::NT = c_str!("win-main");
|
||||
const B_WIN_M_PROP: ffi::NT = c_str!("win-map-prop");
|
||||
const B_WIN_M_TOOL: ffi::NT = c_str!("win-map-tools");
|
||||
const B_WIN_M_VIEW: ffi::NT = c_str!("win-map-view");
|
||||
const E_ACTIVATE: ffi::NT = c_str!("activate");
|
||||
const E_DELETE: ffi::NT = c_str!("delete-event");
|
||||
const E_DESTROY: ffi::NT = c_str!("destroy");
|
||||
const E_DRAW: ffi::NT = c_str!("draw");
|
||||
const E_SHUTDOWN: ffi::NT = c_str!("shutdown");
|
||||
const E_TOGGLE: ffi::NT = c_str!("toggled");
|
||||
const IM_ABOUT: ffi::NT = c_str!("/net/greyserv/maraiah/tycho/tycho2.png");
|
||||
const IM_NOMAP: ffi::NT = c_str!("/net/greyserv/maraiah/tycho/tycho1.png");
|
||||
const PATH_BUILDER: ffi::NT = c_str!("/net/greyserv/maraiah/tycho/ui");
|
||||
const PATH_CSS: ffi::NT = c_str!("/net/greyserv/maraiah/tycho/css");
|
||||
|
||||
// EOF
|
Loading…
Reference in New Issue