use enumx::export::*;
use enumx::predefined::*;
use cex::*;
use once_cell::sync::Lazy;
pub(crate) use std::{
any::TypeId,
cell::{Cell, RefCell},
collections::HashSet,
marker::PhantomData,
mem,
ops::Deref,
os::raw::c_int,
sync::Mutex,
};
#[cfg( feature = "libtk" )]
pub(crate) use std::os::raw::c_char;
pub(crate) use tcl::{
Interpreter,
Obj,
error::{
InterpError,
NullInterp,
TclInitError,
},
};
#[macro_export]
macro_rules! make_tk {
() => { Tk::new( ||() ) }
}
pub mod bitmap;
pub use bitmap::Bitmap;
pub mod error;
pub use error::{
TkError,
TkResult,
};
#[macro_use]
pub mod cmd;
pub use cmd::{
TkRoot,
Widget,
no_arg,
path_seg,
};
pub(crate) use cmd::{
PathOptsWidgets,
};
pub mod key_syms;
pub use key_syms::TkKey;
mod lower;
#[macro_use]
pub mod opt;
use opt::{OptPair, TkOption};
mod option;
pub mod photo;
pub use photo::Photo;
pub mod query;
pub use query::{UpcastFrom, UpcastableWidget, CreatedWidgets};
mod raise;
pub mod ttk_style;
pub mod ttk_widget;
pub use ttk_widget::{
TtkCommonTraits,
TtkState,
TtkStateSpec,
};
pub mod bind;
pub mod event;
pub use event::TkEventSeq;
pub mod image;
pub use image::Image;
mod grid;
mod pack;
mod focus;
mod winfo;
pub mod wm;
pub use wm::{
TkFocusModel,
WmManage,
};
pub mod range;
pub use range::{
TkDefaultStart,
TkDefaultEnd,
};
pub mod traits;
pub use traits::{Delete, TkBBoxTrait, TkEntryTraits, TkXView, TkXViewIndex, TkYView, TkYViewIndex};
pub mod types;
pub use types::{
TkBBox,
TkColor,
TkCoord,
TkHandler,
TkGeometry,
TkDistance,
TkDLine,
TkRGB,
TkRectangle,
TkResizable,
TkRequester,
TkState,
TkSize,
TkScreenName,
TkVisualClass,
TkWindowingSystem,
TtkInsertPos,
TtkTreeviewRegion,
};
pub mod button;
pub use button::TkButton;
pub mod canvas;
pub use canvas::TkCanvas;
pub mod checkbutton;
pub use checkbutton::TkCheckbutton;
pub mod entry;
pub use entry::{
TkEntry,
Index as TkEntryIndex,
};
pub mod frame;
pub use frame::TkFrame;
pub mod label;
pub use label::TkLabel;
pub mod labelframe;
pub use labelframe::TkLabelframe;
pub mod listbox;
pub use listbox::{
Index as TkListboxIndex,
TkListbox,
};
pub mod menu;
pub use menu::{
AddMenus,
Index as TkMenuIndex,
TkMenu,
TkMenuCloneType,
TkMenuEntryType,
};
pub mod menubutton;
pub use menubutton::TkMenubutton;
pub mod message;
pub use message::TkMessage;
pub mod panedwindow;
pub use panedwindow::{TkPanedwindow, TkSashOrHandle};
pub mod radiobutton;
pub use radiobutton::TkRadiobutton;
pub mod scale;
pub use scale::{TkScale, TkScaleCoord, TkScalePart};
pub mod scrollbar;
pub use scrollbar::{TkScrollbar, TkScrollbarElement, TkScrollbarDelta};
pub mod spinbox;
pub use spinbox::{TkSpinbox, TkSpinboxElement, TkSpinboxInvokableElement};
pub mod text;
pub use text::{
TkCmp,
TkDump,
TkText,
TkTextMarkGravity,
TkTextSearch,
TkTextSearchAll,
};
pub mod toplevel;
pub use toplevel::TkToplevel;
pub mod ttk_button;
pub use ttk_button::TtkButton;
pub mod ttk_checkbutton;
pub use ttk_checkbutton::TtkCheckbutton;
pub mod ttk_combobox;
pub use ttk_combobox::{
TtkCombobox,
Index as TtkComboboxIndex,
};
pub mod ttk_entry;
pub use ttk_entry::{
Index as TtkEntryIndex,
TtkEntry,
};
pub mod ttk_frame;
pub use ttk_frame::TtkFrame;
pub mod ttk_label;
pub use ttk_label::TtkLabel;
pub mod ttk_labelframe;
pub use ttk_labelframe::TtkLabelframe;
pub mod ttk_menubutton;
pub use ttk_menubutton::TtkMenubutton;
pub mod ttk_notebook;
pub use ttk_notebook::{TtkNotebook, TtkNotebookTabId};
pub mod ttk_panedwindow;
pub use ttk_panedwindow::TtkPanedwindow;
pub mod ttk_progressbar;
pub use ttk_progressbar::{TtkProgressbar, TtkProgressbarInterval};
pub mod ttk_radiobutton;
pub use ttk_radiobutton::TtkRadiobutton;
pub mod ttk_scale;
pub use ttk_scale::TtkScale;
pub mod ttk_scrollbar;
pub use ttk_scrollbar::TtkScrollbar;
pub mod ttk_separator;
pub use ttk_separator::TtkSeparator;
pub mod ttk_sizegrip;
pub use ttk_sizegrip::TtkSizegrip;
pub mod ttk_spinbox;
pub use ttk_spinbox::TtkSpinbox;
pub mod ttk_treeview;
pub use ttk_treeview::{
Index as TtkTreeviewIndex,
TtkTreeview,
Column as TtkTreeviewColumn,
};
pub mod font;
pub use font::Font;
pub mod ext;
pub use ext::{AddHBox, AddVBox, HBox, HBoxResize, VBox, VBoxResize};
const TEST_MAIN_WINDOW: &'static str = "winfo exists .\0";
pub fn main_loop() {
loop {
unsafe{ clib::Tcl_DoOneEvent( 0 ); }
let no_main_window = TK_INSTANCES.with( |instances| {
let script = TEST_MAIN_WINDOW.as_ptr() as *const _;
for (_, engine) in instances.borrow().iter() {
let tcl_interp = engine.interpreter.as_ptr();
if unsafe{ clib::Tcl_Eval( tcl_interp, script )} == clib::TCL_OK as c_int {
return false;
}
}
true
});
if no_main_window {
break;
}
}
}
pub type InterpResult<T> = Result<T, tcl::error::InterpError>;
#[cfg( feature = "libtk" )]
pub fn main( args: impl Iterator<Item=String>, mut init: clib::Tcl_AppInitProc ) {
let mut v: Vec<_> = args
.map( |arg| std::ffi::CString::new( Vec::<u8>::from( arg ))
.expect( "String should not contain nul character" )
.into_raw() )
.collect();
let argc = v.len() as c_int;
let argv = v.as_mut_ptr() as *mut *mut c_char;
mem::forget( v );
if init == None {
init = Some( tcl_app_init_proc );
}
unsafe {
clib::Tk_MainEx( argc, argv, init, Interpreter::new().unwrap().as_ptr() );
}
}
#[doc( hidden )]
#[cfg( feature = "libtk" )]
unsafe extern "C" fn tcl_app_init_proc( interp: *mut clib::Tcl_Interp ) -> c_int {
const TCL_OK: c_int = clib::TCL_OK as c_int;
let result = clib::Tk_Init( interp );
if result != TCL_OK {
eprintln!( "unable to Initialize Tk!\n" );
return result;
}
return TCL_OK;
}
#[doc( hidden )]
pub struct Engine {
interpreter : Interpreter,
serial : Cell<usize>, }
impl Engine {
fn incr_serial( &self ) -> usize {
self.serial.set( self.serial.get() + 1 );
self.serial.get()
}
}
impl Deref for Engine {
type Target = Interpreter;
fn deref( &self ) -> &Self::Target { &self.interpreter }
}
pub type NotSendSync = PhantomData<*const ()>;
const NOT_SEND_SYNC: NotSendSync = PhantomData;
#[derive( Copy, Clone )]
pub struct Tk<Inst:TkInstance>
{
inst : Inst,
mark : NotSendSync,
}
impl<Inst:TkInstance> Deref for Tk<Inst> {
type Target = Engine;
fn deref( &self ) -> &Self::Target {
TK_INSTANCES.with( |instances| {
let tk_type_id = TypeId::of::<Inst>();
for (type_id, engine) in instances.borrow().iter() {
if &tk_type_id == type_id {
return unsafe{ &*( engine as *const _ )};
}
}
unreachable!()
})
}
}
#[doc( hidden )]
pub trait TkInstance : 'static + Copy + Clone {}
impl<T:'static + Copy + Clone> TkInstance for T {}
thread_local! {
static TK_INSTANCES: RefCell<Vec<(TypeId, Engine)>> = RefCell::new( Vec::new() );
static WIDGET_PATH_SET: RefCell<HashSet<&'static str>> = {
let mut set = HashSet::new();
set.insert( "." );
RefCell::new( set )
};
}
static MUTEX: Lazy<Mutex<()>> = Lazy::new( || Mutex::new(()) );
const TK_INIT_SCRIPT: &'static str = r#"
package require Tk
proc tk_rs_option_menu {pathName varName items} {
global tk_rs_widget_extra_data
set tk_rs_widget_extra_data($pathName) [eval tk_optionMenu {$pathName} {$varName} $items]
}
"#;
impl<Inst> Tk<Inst>
where Inst: 'static + Copy + Clone
{
#[cex]
pub fn new( inst: Inst ) -> Result!( Tk<Inst> throws InterpError, NullInterp, TclInitError ) {
let tk_type_id = TypeId::of::<Inst>();
let not_unique = TK_INSTANCES.with( |instances| instances
.borrow()
.iter()
.find( |(type_id, _)| &tk_type_id == type_id )
.is_some()
);
if not_unique {
panic!( "Tk instance exists already" );
}
let interpreter = Interpreter::new()?;
if interpreter.eval( "package present Tk" ).is_err() {
let m = crate::MUTEX.lock();
interpreter.eval( TK_INIT_SCRIPT )?;
m.ok();
}
let tk = Tk{ inst, mark: NOT_SEND_SYNC };
let engine = Engine{ interpreter, serial: Cell::new(0) };
TK_INSTANCES.with( |instances| {
instances.borrow_mut().push(( tk_type_id, engine ));
});
Ok( tk )
}
pub(crate) fn from_inst( inst: Inst ) -> Tk<Inst> { Tk{ inst, mark: NOT_SEND_SYNC }}
pub(crate) fn make_or_get_path( path: &str ) -> &'static str {
WIDGET_PATH_SET.with( |path_set| {
let mut path_set = path_set.borrow_mut();
match path_set.get( path ) {
Some( path ) => *path,
None => {
let path: &'static str = Box::leak( path.to_owned().into_boxed_str() );
path_set.insert( path );
path
},
}
})
}
}
impl<Inst:TkInstance> Tk<Inst> {
pub fn root( &self ) -> TkRoot<Inst> {
TkRoot(
Widget {
path : ".",
inst : self.inst,
mark : NOT_SEND_SYNC,
}
)
}
pub(crate) fn next_path( &self, parent_path: &str, path: &str ) -> String {
if path.len() == 0 {
let serial = self.deref().incr_serial();
if parent_path == "." {
format!( ".{}", serial )
} else {
format!( "{}.{}", parent_path, serial )
}
} else {
if parent_path == "." {
format!( ".{}", path )
} else {
format!( "{}.{}", parent_path, path )
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cex]
fn it_works() -> Result!( () throws InterpError, NullInterp, TclInitError ) {
let _tk = Tk::new(|| ())?;
let _tk = Tk::new(|| ())?;
Ok( main_loop() )
}
}