@@ -4,18 +4,25 @@ use callback_helpers::{from_void_ptr, to_heap_ptr};
4
4
use controls:: Window ;
5
5
use std:: ffi:: CString ;
6
6
use std:: os:: raw:: { c_int, c_void} ;
7
+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
7
8
use ui_sys:: { self , uiMenu, uiMenuItem, uiWindow} ;
8
9
use UI ;
9
10
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,
11
15
/// the text on `MenuItem`s cannot be changed after creation.
12
16
#[ derive( Clone ) ]
13
17
pub struct MenuItem {
14
18
ui_menu_item : * mut uiMenuItem ,
15
19
}
16
20
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.
19
26
#[ derive( Clone ) ]
20
27
pub struct Menu {
21
28
ui_menu : * mut uiMenu ,
@@ -77,33 +84,49 @@ impl MenuItem {
77
84
}
78
85
79
86
impl 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
+ } )
86
99
}
87
100
}
88
101
}
89
102
90
103
/// 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 {
92
109
unsafe {
93
110
let c_string = CString :: new ( name. as_bytes ( ) . to_vec ( ) ) . unwrap ( ) ;
94
- MenuItem {
111
+ Some ( MenuItem {
95
112
ui_menu_item : ui_sys:: uiMenuAppendItem ( self . ui_menu , c_string. as_ptr ( ) ) ,
96
- }
113
+ } )
114
+ }
97
115
}
98
116
}
99
117
100
118
/// 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 {
102
124
unsafe {
103
125
let c_string = CString :: new ( name. as_bytes ( ) . to_vec ( ) ) . unwrap ( ) ;
104
- MenuItem {
126
+ Some ( MenuItem {
105
127
ui_menu_item : ui_sys:: uiMenuAppendCheckItem ( self . ui_menu , c_string. as_ptr ( ) ) ,
106
- }
128
+ } )
129
+ }
107
130
}
108
131
}
109
132
@@ -112,3 +135,15 @@ impl Menu {
112
135
unsafe { ui_sys:: uiMenuAppendSeparator ( self . ui_menu ) }
113
136
}
114
137
}
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