@@ -4,18 +4,25 @@ use callback_helpers::{from_void_ptr, to_heap_ptr};
44use controls:: Window ;
55use std:: ffi:: CString ;
66use std:: os:: raw:: { c_int, c_void} ;
7+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
78use ui_sys:: { self , uiMenu, uiMenuItem, uiWindow} ;
89use UI ;
910
10- /// A `MenuItem` represents an item that is shown in a `Menu`. Note that, unlike many controls,
11+ pub static HAS_FINALIZED_MENUS : AtomicBool = AtomicBool :: new ( false ) ;
12+
13+ /// A `MenuItem` represents an item that is shown in a `Menu`.
14+ /// Note that, unlike many controls,
1115/// the text on `MenuItem`s cannot be changed after creation.
1216#[ derive( Clone ) ]
1317pub struct MenuItem {
1418 ui_menu_item : * mut uiMenuItem ,
1519}
1620
17- /// A `Menu` represents one of the top-level menus at the top of a window. As that bar is unique
18- /// per application, creating a new `Menu` shows it on all windows that support displaying menus.
21+ /// A `Menu` represents one of the top-level menus at the top of a window.
22+ /// That bar is unique per application, and creating a new `Menu` shows it
23+ /// on all windows that support displaying menus.
24+ ///
25+ /// Once windows have been created, no more menus can be created.
1926#[ derive( Clone ) ]
2027pub struct Menu {
2128 ui_menu : * mut uiMenu ,
@@ -77,33 +84,49 @@ impl MenuItem {
7784}
7885
7986impl Menu {
80- /// Creates a new menu with the given name to be displayed in the menubar at the top of the window.
81- pub fn new ( _ctx : & UI , name : & str ) -> Menu {
82- unsafe {
83- let c_string = CString :: new ( name. as_bytes ( ) . to_vec ( ) ) . unwrap ( ) ;
84- Menu {
85- ui_menu : ui_sys:: uiNewMenu ( c_string. as_ptr ( ) ) ,
87+ /// Creates a new menu with the given name to be displayed in the menubar
88+ /// at the top of all windows with a menubar.
89+ ///
90+ /// This is possible only if menus have not been finalized.
91+ pub fn new ( _ctx : & UI , name : & str ) -> Option < Menu > {
92+ if HAS_FINALIZED_MENUS . load ( Ordering :: SeqCst ) { None }
93+ else {
94+ unsafe {
95+ let c_string = CString :: new ( name. as_bytes ( ) . to_vec ( ) ) . unwrap ( ) ;
96+ Some ( Menu {
97+ ui_menu : ui_sys:: uiNewMenu ( c_string. as_ptr ( ) ) ,
98+ } )
8699 }
87100 }
88101 }
89102
90103 /// Adds a new item with the given name to the menu.
91- pub fn append_item ( & self , name : & str ) -> MenuItem {
104+ ///
105+ /// This is possible only if menus have not been finalized.
106+ pub fn append_item ( & self , name : & str ) -> Option < MenuItem > {
107+ if HAS_FINALIZED_MENUS . load ( Ordering :: SeqCst ) { None }
108+ else {
92109 unsafe {
93110 let c_string = CString :: new ( name. as_bytes ( ) . to_vec ( ) ) . unwrap ( ) ;
94- MenuItem {
111+ Some ( MenuItem {
95112 ui_menu_item : ui_sys:: uiMenuAppendItem ( self . ui_menu , c_string. as_ptr ( ) ) ,
96- }
113+ } )
114+ }
97115 }
98116 }
99117
100118 /// Adds a new togglable (checkbox) item with the given name to the menu.
101- pub fn append_check_item ( & self , name : & str ) -> MenuItem {
119+ ///
120+ /// This is possible only if menus have not been finalized.
121+ pub fn append_check_item ( & self , name : & str ) -> Option < MenuItem > {
122+ if HAS_FINALIZED_MENUS . load ( Ordering :: SeqCst ) { None }
123+ else {
102124 unsafe {
103125 let c_string = CString :: new ( name. as_bytes ( ) . to_vec ( ) ) . unwrap ( ) ;
104- MenuItem {
126+ Some ( MenuItem {
105127 ui_menu_item : ui_sys:: uiMenuAppendCheckItem ( self . ui_menu , c_string. as_ptr ( ) ) ,
106- }
128+ } )
129+ }
107130 }
108131 }
109132
@@ -112,3 +135,15 @@ impl Menu {
112135 unsafe { ui_sys:: uiMenuAppendSeparator ( self . ui_menu ) }
113136 }
114137}
138+
139+ #[ test]
140+ fn cannot_change_menus_late ( ) {
141+ use crate :: prelude:: * ;
142+ let ui = UI :: init ( ) . expect ( "failed to init" ) ;
143+ let mut menu = Menu :: new ( & ui, "menu" ) . unwrap ( ) ;
144+ assert ! ( menu. append_item( "test item" ) . is_some( ) ) ;
145+ let win = Window :: new ( & ui, "Test App" , 200 , 200 , WindowType :: HasMenubar ) ;
146+ assert ! ( Menu :: new( & ui, "menu2" ) . is_none( ) ) ;
147+ assert ! ( menu. append_item( "test item 2" ) . is_none( ) ) ;
148+ }
149+
0 commit comments