make flags display on properties pane
parent
3b8790b442
commit
f895dac2b8
|
@ -122,11 +122,14 @@ bitflags! {
|
|||
#[cfg_attr(feature = "serde_obj", derive(serde::Serialize))]
|
||||
pub struct MsnFlags: u16
|
||||
{
|
||||
const EXTERMINATION = 1;
|
||||
const EXPLORATION = 1 << 1;
|
||||
const RETRIEVAL = 1 << 2;
|
||||
const REPAIR = 1 << 3;
|
||||
const RESCUE = 1 << 4;
|
||||
const EXTERMINATION = 1;
|
||||
const EXPLORATION = 1 << 1;
|
||||
const RETRIEVAL = 1 << 2;
|
||||
const REPAIR = 1 << 3;
|
||||
const RESCUE = 1 << 4;
|
||||
const M1_EXPLORATION = 1 << 5;
|
||||
const M1_RESCUE = 1 << 6;
|
||||
const M1_REPAIR = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,21 +5,41 @@
|
|||
//! edit it.
|
||||
|
||||
mod block;
|
||||
mod state;
|
||||
|
||||
pub use block::*;
|
||||
|
||||
use super::{color, draw::*};
|
||||
use crate::durandal::image::*;
|
||||
use crate::{durandal::image::*, marathon::{machdr, wad}};
|
||||
|
||||
impl MapEditor
|
||||
{
|
||||
/// Opens the editor with a new empty map.
|
||||
#[inline]
|
||||
pub fn open_new(&mut self) {self.map = Some(state::OpenMap::default());}
|
||||
/// Opens the editor with a new map.
|
||||
pub fn open_new(&mut self)
|
||||
{
|
||||
*self = Self::default();
|
||||
}
|
||||
|
||||
/// Opens the editor with an existing map.
|
||||
pub fn open_buf(&mut self, b: &[u8])
|
||||
{
|
||||
self.map = Some(state::OpenMap::open_buf(b));
|
||||
// TODO: handle errors gracefully
|
||||
let b = &b[machdr::try_mac_header(b)..];
|
||||
let wad = wad::read_wad(b).unwrap();
|
||||
|
||||
let ent = wad.entries.iter().nth(0).unwrap().1;
|
||||
let info = ent.chunks.iter().find_map(|cnk| {
|
||||
match cnk {
|
||||
wad::Chunk::Minf(info) => Some(info),
|
||||
_ => None,
|
||||
}
|
||||
}).unwrap().clone();
|
||||
|
||||
let block = Block{info};
|
||||
|
||||
dbg!(&block);
|
||||
|
||||
*self = Self{blocks: vec![block],
|
||||
tools: Self::default_tools()};
|
||||
}
|
||||
|
||||
/// Draws the screen for this editor state.
|
||||
|
@ -27,55 +47,83 @@ impl MapEditor
|
|||
where D: DrawArea<NativeImage = I>,
|
||||
I: CacheImage
|
||||
{
|
||||
/*
|
||||
let dw = d.w();
|
||||
let dh = d.h();
|
||||
let iw = im.w();
|
||||
let ih = im.h();
|
||||
|
||||
match &self.map {
|
||||
None => {
|
||||
let tx_top = "Map Required To Proceed";
|
||||
let tx_bot = "CAS.qterm//CyberAcme Systems Inc.";
|
||||
let tx_top = "Map Required To Proceed";
|
||||
let tx_bot = "CAS.qterm//CyberAcme Systems Inc.";
|
||||
|
||||
d.clear(Color16::new(0, 0, 0));
|
||||
d.clear(Color16::new(0, 0, 0));
|
||||
|
||||
d.image((dw / 2 - iw / 2, dh / 2 - ih / 2), im);
|
||||
d.image((dw / 2 - iw / 2, dh / 2 - ih / 2), im);
|
||||
|
||||
d.rect(Rect{x: 0, y: 0, w: dw, h: 18}, color::DARK_RED);
|
||||
d.text((4, 14), tx_top, color::RED);
|
||||
d.rect(Rect{x: 0, y: 0, w: dw, h: 18}, color::DARK_RED);
|
||||
d.text((4, 0), tx_top, color::RED);
|
||||
|
||||
d.rect(Rect{x: 0, y: dh - 18, w: dw, h: 18}, color::DARK_RED);
|
||||
d.text((4, dh - 4), tx_bot, color::RED);
|
||||
}
|
||||
Some(st) => {
|
||||
let text = &format!("tool: {:?}", st.tool());
|
||||
d.rect(Rect{x: 0, y: dh - 18, w: dw, h: 18}, color::DARK_RED);
|
||||
d.text((4, dh - 16), tx_bot, color::RED);
|
||||
*/
|
||||
|
||||
d.clear(Color16::new(0, 0, 0));
|
||||
d.text((dw / 2, dh / 2), text, color::RED);
|
||||
}
|
||||
}
|
||||
let _ = im;
|
||||
|
||||
let text = &format!("{:#?}", &self);
|
||||
|
||||
d.clear(Color16::new(0, 0, 0));
|
||||
d.text((0, 0), text, color::RED);
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is closed.
|
||||
#[inline]
|
||||
pub fn is_closed(&self) -> bool {self.map.is_none()}
|
||||
// Returns the default tools.
|
||||
const fn default_tools() -> (Tool, Tool) {(Tool::Points, Tool::Lines)}
|
||||
|
||||
/// Returns `true` if `self` is opened.
|
||||
#[inline]
|
||||
pub fn is_opened(&self) -> bool {self.map.is_some()}
|
||||
/// Returns a reference to the current block.
|
||||
pub fn cur_block(&self) -> &Block {self.blocks.last().unwrap()}
|
||||
|
||||
/// Pushes a new block.
|
||||
pub fn push_block(&mut self, blk: Block) {self.blocks.push(blk);}
|
||||
|
||||
/// Returns the current tool.
|
||||
pub fn tool(&self) -> &Tool {&self.tools.0}
|
||||
|
||||
/// Sets the current tool, and returns the previous one.
|
||||
pub fn set_tool(&mut self, t: Tool) -> &Tool
|
||||
{
|
||||
self.tools.1 = self.tools.0.clone();
|
||||
self.tools.0 = t;
|
||||
|
||||
&self.tools.1
|
||||
}
|
||||
|
||||
/// Returns true if the current map is unclean and needs to be saved.
|
||||
pub fn unclean(&self) -> bool {true}
|
||||
}
|
||||
|
||||
impl Default for MapEditor
|
||||
{
|
||||
#[inline]
|
||||
fn default() -> Self {Self{map: None}}
|
||||
fn default() -> Self
|
||||
{
|
||||
Self{blocks: vec![Block::default()], tools: Self::default_tools()}
|
||||
}
|
||||
}
|
||||
|
||||
/// An entire map editor, which may be opened or closed. Holds state which
|
||||
/// outlives the opened map state.
|
||||
/// The state of an opened map editor.
|
||||
#[derive(Debug)]
|
||||
pub struct MapEditor
|
||||
{
|
||||
map: Option<state::OpenMap>,
|
||||
blocks: Vec<Block>,
|
||||
tools: (Tool, Tool),
|
||||
}
|
||||
|
||||
/// A tool in the map editor.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Tool
|
||||
{
|
||||
Points,
|
||||
Lines,
|
||||
Polygons,
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -8,9 +8,9 @@ impl Default for Block
|
|||
|
||||
/// Copyable, versioned map state.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct Block
|
||||
pub struct Block
|
||||
{
|
||||
pub(super) info: map::minf::Minf,
|
||||
pub info: map::minf::Minf,
|
||||
}
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
//! Map editor state.
|
||||
|
||||
use super::block;
|
||||
use crate::marathon::{machdr, wad};
|
||||
|
||||
impl OpenMap
|
||||
{
|
||||
pub(super) fn open_buf(b: &[u8]) -> Self
|
||||
{
|
||||
// TODO: handle errors gracefully
|
||||
let b = &b[machdr::try_mac_header(b)..];
|
||||
let wad = wad::read_wad(b).unwrap();
|
||||
|
||||
let ent = wad.entries.iter().nth(0).unwrap().1;
|
||||
let info = ent.chunks.iter().find_map(|cnk| {
|
||||
match cnk {
|
||||
wad::Chunk::Minf(info) => Some(info),
|
||||
_ => None,
|
||||
}
|
||||
}).unwrap().clone();
|
||||
|
||||
let block = block::Block{info};
|
||||
|
||||
dbg!(&block);
|
||||
|
||||
Self{blocks: vec![block],
|
||||
tools: Self::default_tools()}
|
||||
}
|
||||
|
||||
pub(super) fn cur_block(&self) -> &block::Block
|
||||
{
|
||||
self.blocks.last().unwrap()
|
||||
}
|
||||
|
||||
pub(super) fn cur_block_mut(&mut self) -> &mut block::Block
|
||||
{
|
||||
self.blocks.last_mut().unwrap()
|
||||
}
|
||||
|
||||
pub(super) fn tool(&self) -> &Tool {&self.tools.0}
|
||||
|
||||
pub(super) fn set_tool(&mut self, t: Tool) -> &Tool
|
||||
{
|
||||
self.tools.1 = self.tools.0.clone();
|
||||
self.tools.0 = t;
|
||||
|
||||
&self.tools.1
|
||||
}
|
||||
|
||||
const fn default_tools() -> (Tool, Tool) {(Tool::Points, Tool::Lines)}
|
||||
}
|
||||
|
||||
impl Default for OpenMap
|
||||
{
|
||||
#[inline]
|
||||
fn default() -> Self
|
||||
{
|
||||
Self{blocks: vec![block::Block::default()],
|
||||
tools: Self::default_tools()}
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of an opened map editor.
|
||||
pub(super) struct OpenMap
|
||||
{
|
||||
blocks: Vec<block::Block>,
|
||||
tools: (Tool, Tool),
|
||||
}
|
||||
|
||||
/// A tool in the map editor.
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) enum Tool
|
||||
{
|
||||
Points,
|
||||
Lines,
|
||||
Polygons,
|
||||
}
|
||||
|
||||
// EOF
|
|
@ -19,6 +19,7 @@ gtk-sys = {version = "0.8", features = ["v3_16"]}
|
|||
maraiah = {path = "../.."}
|
||||
memmap = "0.7"
|
||||
pango-sys = "0.8"
|
||||
pangocairo-sys = "0.9"
|
||||
|
||||
[[bin]]
|
||||
name = "tycho"
|
||||
|
|
|
@ -1,15 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.1
|
||||
|
||||
Author: Alison Sanderson
|
||||
|
||||
-->
|
||||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name Maraiah Tycho -->
|
||||
<!-- interface-description Tycho map editor for Maraiah. -->
|
||||
<!-- interface-authors Alison Sanderson -->
|
||||
<object class="GtkAdjustment" id="adj-map-horz">
|
||||
<property name="upper">100</property>
|
||||
<property name="step_increment">1</property>
|
||||
|
@ -226,151 +218,135 @@ Author: Alison Sanderson
|
|||
<property name="can_focus">False</property>
|
||||
<property name="left_padding">12</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<object class="GtkGrid" id="con-f-ent">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">Solo</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">Solo</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in single-player.</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">Co-op</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player co-operative.</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">Carnage</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player Carnage.</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">KTMWTB</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player Kill The Man With The Ball</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in single-player.</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">Co-op</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">King Of The Hill</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player King of the Hill.</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">Defense</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player Defense.</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">Rugby</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player Rugby.</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">Capture The Flag</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player Capture The Flag.</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player co-operative.</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">Carnage</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player Carnage.</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">KTMWTB</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player Kill The Man With The Ball</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">King Of The Hill</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player King of the Hill.</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">Defense</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player Defense.</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">Rugby</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player Rugby.</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCheckButton">
|
||||
<property name="label" translatable="yes">Capture The Flag</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="tooltip_text" translatable="yes">The map can be played in multi-player Capture The Flag.</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="vexpand">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -411,7 +387,7 @@ Author: Alison Sanderson
|
|||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<object class="GtkBox" id="con-f-msn">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
|
@ -583,7 +559,7 @@ Author: Alison Sanderson
|
|||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<object class="GtkBox" id="con-f-env">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
|
|
|
@ -1,279 +1,9 @@
|
|||
//! Implemented interfaces for Rozinante.
|
||||
//! Interfaces to the system.
|
||||
|
||||
use cairo_sys::*;
|
||||
use gdk_pixbuf_sys::*;
|
||||
use gdk_sys::*;
|
||||
use glib_sys::*;
|
||||
use gobject_sys::*;
|
||||
use gtk_sys::*;
|
||||
use maraiah::{c_str,
|
||||
durandal::{ffi, image::*},
|
||||
rozinante::{draw::*, editor}};
|
||||
use std::marker::PhantomData;
|
||||
pub mod cairo;
|
||||
pub mod editor;
|
||||
pub mod glib;
|
||||
|
||||
/// 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 CacheImage for CrImage
|
||||
{
|
||||
fn w(&self) -> Coord {unsafe {gdk_pixbuf_get_width(self.0) as Coord}}
|
||||
fn h(&self) -> Coord {unsafe {gdk_pixbuf_get_height(self.0) as Coord}}
|
||||
}
|
||||
|
||||
impl CrDrawArea
|
||||
{
|
||||
pub const fn new(ctx: *mut cairo_t, w: f64, h: f64) -> Self
|
||||
{
|
||||
CrDrawArea{ctx, w: w as Coord, h: h as Coord}
|
||||
}
|
||||
}
|
||||
|
||||
impl DrawArea for CrDrawArea
|
||||
{
|
||||
type NativeImage = CrImage;
|
||||
|
||||
fn w(&self) -> Coord {self.w}
|
||||
fn h(&self) -> Coord {self.h}
|
||||
|
||||
fn clear(&mut self, cr: impl Color)
|
||||
{
|
||||
self.rect(Rect{x: 0, y: 0, w: self.w(), h: self.h()}, cr);
|
||||
|
||||
let sl = FONT_SLANT_NORMAL;
|
||||
let wt = FONT_WEIGHT_NORMAL;
|
||||
|
||||
unsafe {
|
||||
cairo_select_font_face(self.ctx, c_str!("Monospace"), sl, wt);
|
||||
cairo_set_font_size(self.ctx, 14.0);
|
||||
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: Point, p2: 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: 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: 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 text = ffi::CString::new(text).unwrap();
|
||||
|
||||
unsafe {
|
||||
cairo_set_source_rgb(self.ctx, r, g, b);
|
||||
cairo_move_to(self.ctx, x, y);
|
||||
cairo_show_text(self.ctx, text.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
fn image(&mut self, pos: 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MapEditor
|
||||
{
|
||||
/// Propagates updated information to widgets.
|
||||
pub fn cause_update(&mut self)
|
||||
{
|
||||
unsafe {
|
||||
gtk_widget_queue_draw(*self.draw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for MapEditor
|
||||
{
|
||||
type Target = editor::MapEditor;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {&self.edit}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for MapEditor
|
||||
{
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {&mut self.edit}
|
||||
}
|
||||
|
||||
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);}}
|
||||
}
|
||||
|
||||
/// An image for a `CrDrawArea`.
|
||||
pub struct CrImage(pub *const GdkPixbuf);
|
||||
|
||||
/// A `DrawArea` for a Cairo surface.
|
||||
pub struct CrDrawArea
|
||||
{
|
||||
ctx: *mut cairo_t,
|
||||
w: Coord,
|
||||
h: Coord,
|
||||
}
|
||||
|
||||
/// Specialized map editor which has callbacks for frontend purposes.
|
||||
pub struct MapEditor
|
||||
{
|
||||
pub edit: editor::MapEditor,
|
||||
pub draw: Refc<'static, GtkWidget>,
|
||||
}
|
||||
|
||||
/// A runtime reference to the map editor.
|
||||
pub type MapEditorRef = std::cell::RefCell<MapEditor>;
|
||||
|
||||
/// 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,
|
||||
}
|
||||
pub use self::{cairo::*, editor::*, glib::*};
|
||||
|
||||
// EOF
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
//! 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
|
|
@ -0,0 +1,148 @@
|
|||
//! Map editor interface.
|
||||
|
||||
use super::glib::*;
|
||||
use gobject_sys::*;
|
||||
use gtk_sys::*;
|
||||
use maraiah::{durandal::ffi, marathon::map, rozinante::editor};
|
||||
|
||||
fn each_flag<T, F>(buttons: &[PropFlag], ordering: &[T], mut f: F)
|
||||
where F: FnMut(&PropFlag, &T)
|
||||
{
|
||||
for (flg, ord) in buttons.iter().zip(ordering) {
|
||||
f(flg, ord);
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh_flags<T, F>(mut f: F) -> impl FnMut(&PropFlag, &T)
|
||||
where F: FnMut(&T) -> bool
|
||||
{
|
||||
move |flg, ord| {
|
||||
unsafe {
|
||||
g_signal_handler_block(*flg.w as _, flg.h);
|
||||
|
||||
gtk_toggle_button_set_active(*flg.w, f(ord).into());
|
||||
|
||||
g_signal_handler_unblock(*flg.w as _, flg.h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_flags<T, F>(mut f: F) -> impl FnMut(&PropFlag, &T)
|
||||
where F: FnMut(&T, bool)
|
||||
{
|
||||
move |flg, ord| f(ord, unsafe {gtk_toggle_button_get_active(*flg.w)} != 0)
|
||||
}
|
||||
|
||||
impl MapEditor
|
||||
{
|
||||
/// Propagates all updated map information to widgets.
|
||||
pub fn cause_refresh(&self)
|
||||
{
|
||||
self.cause_refresh_view();
|
||||
self.cause_refresh_props();
|
||||
}
|
||||
|
||||
/// Propagates updated map view information to widgets.
|
||||
pub fn cause_refresh_view(&self)
|
||||
{
|
||||
unsafe {
|
||||
gtk_widget_queue_draw(*self.draw);
|
||||
}
|
||||
}
|
||||
|
||||
/// Propagates updated map property information to widgets.
|
||||
pub fn cause_refresh_props(&self)
|
||||
{
|
||||
let inf = &self.cur_block().info;
|
||||
|
||||
each_flag(&self.fent, &O_ENT, refresh_flags(|&f| inf.entr_flags.contains(f)));
|
||||
each_flag(&self.fenv, &O_ENV, refresh_flags(|&f| inf.envi_flags.contains(f)));
|
||||
each_flag(&self.fmsn, &O_MSN, refresh_flags(|&f| inf.miss_flags.contains(f)));
|
||||
}
|
||||
|
||||
/// Propagates updated map property information to the editor state.
|
||||
pub fn cause_update_props(&mut self)
|
||||
{
|
||||
let mut blk = self.cur_block().clone();
|
||||
let inf = &mut blk.info;
|
||||
|
||||
each_flag(&self.fent, &O_ENT, set_flags(|&f, s| inf.entr_flags.set(f, s)));
|
||||
each_flag(&self.fenv, &O_ENV, set_flags(|&f, s| inf.envi_flags.set(f, s)));
|
||||
each_flag(&self.fmsn, &O_MSN, set_flags(|&f, s| inf.miss_flags.set(f, s)));
|
||||
|
||||
self.push_block(blk);
|
||||
self.cause_refresh_view();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for MapEditor
|
||||
{
|
||||
type Target = editor::MapEditor;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {&self.edit}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for MapEditor
|
||||
{
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {&mut self.edit}
|
||||
}
|
||||
|
||||
/// Specialized map editor which has callbacks for frontend purposes.
|
||||
pub struct MapEditor
|
||||
{
|
||||
pub edit: editor::MapEditor,
|
||||
pub draw: Refc<'static, GtkWidget>,
|
||||
pub fent: Vec<PropFlag>,
|
||||
pub fenv: Vec<PropFlag>,
|
||||
pub fmsn: Vec<PropFlag>,
|
||||
}
|
||||
|
||||
/// A runtime reference to the map editor.
|
||||
pub type MapEditorRef = std::cell::RefCell<MapEditor>;
|
||||
|
||||
pub struct PropFlag
|
||||
{
|
||||
pub w: Refc<'static, GtkToggleButton>,
|
||||
pub h: ffi::c_ulong,
|
||||
}
|
||||
|
||||
// NOTE: this is flipped because of GTK weirdness. don't touch
|
||||
const O_ENT: [map::minf::EntFlags; 8] = [
|
||||
map::minf::EntFlags::CTF,
|
||||
map::minf::EntFlags::RUGBY,
|
||||
map::minf::EntFlags::DEFENSE,
|
||||
map::minf::EntFlags::KOTH,
|
||||
map::minf::EntFlags::KTMWTB,
|
||||
map::minf::EntFlags::CARNAGE,
|
||||
map::minf::EntFlags::CO_OP,
|
||||
map::minf::EntFlags::SOLO,
|
||||
];
|
||||
|
||||
const O_ENV: [map::minf::EnvFlags; 11] = [
|
||||
map::minf::EnvFlags::VACUUM,
|
||||
map::minf::EnvFlags::MAGNETIC,
|
||||
map::minf::EnvFlags::REBELLION,
|
||||
map::minf::EnvFlags::LOW_GRAV,
|
||||
map::minf::EnvFlags::M1_GLUE,
|
||||
map::minf::EnvFlags::LAVA_FLOOR,
|
||||
map::minf::EnvFlags::REBELLION2,
|
||||
map::minf::EnvFlags::MUSIC,
|
||||
map::minf::EnvFlags::TERM_PAUSE,
|
||||
map::minf::EnvFlags::M1_MONSTER,
|
||||
map::minf::EnvFlags::M1_WEPS,
|
||||
];
|
||||
|
||||
const O_MSN: [map::minf::MsnFlags; 8] = [
|
||||
map::minf::MsnFlags::EXTERMINATION,
|
||||
map::minf::MsnFlags::EXPLORATION,
|
||||
map::minf::MsnFlags::RETRIEVAL,
|
||||
map::minf::MsnFlags::REPAIR,
|
||||
map::minf::MsnFlags::RESCUE,
|
||||
map::minf::MsnFlags::M1_EXPLORATION,
|
||||
map::minf::MsnFlags::M1_RESCUE,
|
||||
map::minf::MsnFlags::M1_REPAIR,
|
||||
];
|
||||
|
||||
// EOF
|
|
@ -0,0 +1,113 @@
|
|||
//! 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
|
|
@ -1,6 +1,6 @@
|
|||
mod interfaces;
|
||||
|
||||
use crate::interfaces::*;
|
||||
use self::interfaces::*;
|
||||
use gdk_pixbuf_sys::*;
|
||||
use gdk_sys::*;
|
||||
use gio_sys::*;
|
||||
|
@ -20,6 +20,9 @@ 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");
|
||||
|
@ -31,6 +34,7 @@ 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");
|
||||
|
@ -40,7 +44,7 @@ const PATH_CSS: ffi::NT = c_str!("/net/greyserv/maraiah/tycho/css");
|
|||
unsafe extern "C" fn app_activate(app: *mut GtkApplication, _: gpointer)
|
||||
{
|
||||
// Callback to finalize the reference.
|
||||
unsafe extern "C" fn c_done(_: *mut GtkWidget, ptr: gpointer)
|
||||
unsafe extern "C" fn c_done(_: *mut GApplication, ptr: gpointer)
|
||||
{
|
||||
let edit = Rc::from_raw(ptr as *const MapEditorRef);
|
||||
assert_eq!(Rc::strong_count(&edit), 1);
|
||||
|
@ -49,11 +53,16 @@ unsafe extern "C" fn app_activate(app: *mut GtkApplication, _: gpointer)
|
|||
let b = Refc::new(gtk_builder_new_from_resource(PATH_BUILDER));
|
||||
|
||||
let edit = MapEditor{edit: Default::default(),
|
||||
draw: Refc::own(get_obj(&b, B_DRAW_AREA))};
|
||||
draw: Refc::own(get_obj(&b, B_DRAW_AREA)),
|
||||
fent: get_flag_fields(&b, B_CON_F_ENT),
|
||||
fenv: get_flag_fields(&b, B_CON_F_ENV),
|
||||
fmsn: get_flag_fields(&b, B_CON_F_MSN)};
|
||||
|
||||
let edit = RefCell::new(edit);
|
||||
let edit = Rc::new(edit);
|
||||
|
||||
setup_css();
|
||||
setup_toggles(edit.clone());
|
||||
setup_draw_area(&b, edit.clone());
|
||||
setup_win_map_view(&b);
|
||||
setup_win_map_tools(&b);
|
||||
|
@ -64,6 +73,45 @@ unsafe extern "C" fn app_activate(app: *mut GtkApplication, _: gpointer)
|
|||
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<PropFlag>
|
||||
{
|
||||
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(PropFlag{w: Refc::own(obj as _), h: 0});
|
||||
});
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
unsafe fn setup_toggles(edit: Rc<MapEditorRef>)
|
||||
{
|
||||
let mut ed = edit.borrow_mut();
|
||||
connect_toggle(edit.clone(), ed.fent.iter_mut());
|
||||
connect_toggle(edit.clone(), ed.fenv.iter_mut());
|
||||
connect_toggle(edit.clone(), ed.fmsn.iter_mut());
|
||||
}
|
||||
|
||||
unsafe fn connect_toggle<'a, I>(edit: Rc<MapEditorRef>, it: I)
|
||||
where I: Iterator<Item = &'a mut PropFlag>
|
||||
{
|
||||
unsafe extern "C" fn c_toggled(_: *mut GtkToggleButton, edit: gpointer)
|
||||
{
|
||||
let edit = &*(edit as *const MapEditorRef);
|
||||
edit.borrow_mut().cause_update_props();
|
||||
}
|
||||
|
||||
for flg in it {
|
||||
let erf = connect_ref(*flg.w, edit.clone());
|
||||
flg.h = connect(*flg.w, E_TOGGLE, c_toggled as _, erf);
|
||||
}
|
||||
}
|
||||
|
||||
// Sets up the map view window's drawing area.
|
||||
unsafe fn setup_draw_area(b: &Refc<GtkBuilder>, edit: Rc<MapEditorRef>)
|
||||
{
|
||||
|
@ -197,40 +245,37 @@ unsafe fn setup_explicit_drop(b: &Refc<GtkBuilder>, win: *mut GtkWindow)
|
|||
let mut exp_del = Vec::new();
|
||||
|
||||
// so, we get all of the objects from the builder, and iterate through them
|
||||
let head = gtk_builder_get_objects(**b);
|
||||
let mut lst = &*head;
|
||||
let head = ListS::new(gtk_builder_get_objects(**b));
|
||||
|
||||
loop {
|
||||
let obj = lst.data as *mut GObject;
|
||||
get_typed_from(head, gtk_window_get_type(), |obj| {
|
||||
let obj = obj as *mut GtkWindow;
|
||||
|
||||
// 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 _, gtk_window_get_type()) != 0 {
|
||||
let owin = obj as *mut GtkWindow;
|
||||
|
||||
if owin != win {
|
||||
exp_del.push(owin);
|
||||
}
|
||||
if obj != win {
|
||||
exp_del.push(obj);
|
||||
}
|
||||
|
||||
let nx = lst.next;
|
||||
|
||||
if nx != ffi::null_mut() {
|
||||
lst = &*nx;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_slist_free(head);
|
||||
});
|
||||
|
||||
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,
|
||||
|
@ -248,7 +293,7 @@ unsafe fn setup_win_main(b: &Refc<GtkBuilder>,
|
|||
let edit = &*(edit as *const MapEditorRef);
|
||||
let mut edit = edit.borrow_mut();
|
||||
|
||||
if edit.is_opened() {
|
||||
if edit.unclean() {
|
||||
let titl = c_str!("Confirm");
|
||||
let text = c_str!("Are you sure you want to create a new project? \
|
||||
Unsaved data may be lost.");
|
||||
|
@ -259,7 +304,7 @@ unsafe fn setup_win_main(b: &Refc<GtkBuilder>,
|
|||
}
|
||||
|
||||
edit.open_new();
|
||||
edit.cause_update();
|
||||
edit.cause_refresh();
|
||||
}
|
||||
|
||||
// Callback to open an existing map when the "Open" button is pressed.
|
||||
|
@ -268,7 +313,7 @@ unsafe fn setup_win_main(b: &Refc<GtkBuilder>,
|
|||
let edit = &*(edit as *const MapEditorRef);
|
||||
let mut edit = edit.borrow_mut();
|
||||
|
||||
if edit.is_opened() {
|
||||
if edit.unclean() {
|
||||
let titl = c_str!("Confirm");
|
||||
let text = c_str!("Are you sure you want to open this project? \
|
||||
Unsaved data may be lost.");
|
||||
|
@ -283,7 +328,7 @@ unsafe fn setup_win_main(b: &Refc<GtkBuilder>,
|
|||
let fp = std::fs::File::open(&path).unwrap();
|
||||
let mm = memmap::Mmap::map(&fp).unwrap();
|
||||
edit.open_buf(&mm);
|
||||
edit.cause_update();
|
||||
edit.cause_refresh();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -427,9 +472,10 @@ unsafe fn get_obj<T>(b: &Refc<GtkBuilder>, name: ffi::NT) -> *mut T
|
|||
|
||||
// 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);
|
||||
g_signal_connect_data(obj as _, name, cb, d as _, None, 0)
|
||||
}
|
||||
|
||||
// Loads a `Pixbuf` from a resource.
|
||||
|
|
Loading…
Reference in New Issue