1 // Written in the D programming language.
2 /++
3  + Authors: KanzakiKino
4  + Copyright: KanzakiKino 2018
5  + License: LGPL-3.0
6 ++/
7 module w4d.widget.input.select;
8 import w4d.parser.colorset,
9        w4d.style.rect,
10        w4d.style.scalar,
11        w4d.task.window,
12        w4d.widget.popup.menu,
13        w4d.widget.base,
14        w4d.widget.panel,
15        w4d.event,
16        w4d.exception;
17 import g4d.glfw.cursor;
18 import gl3n.linalg;
19 
20 /// A handler that handles changing selection.
21 alias SelectChanged = EventHandler!( void, int );
22 
23 /// A widget of select input.
24 class SelectInputWidget : PanelWidget
25 {
26     protected class CustomPopupMenuWidget : PopupMenuWidget
27     {
28         override void handlePopup ( bool opened, WindowContext ctx )
29         {
30             super.handlePopup( opened, ctx );
31             _menuOpened = opened;
32         }
33         this ()
34         {
35             super();
36         }
37     }
38 
39     protected CustomPopupMenuWidget _menu;
40     protected Widget                _selected;
41     protected bool                  _menuOpened;
42 
43     ///
44     override @property Widget[] children ()
45     {
46         if ( _menuOpened ) return [];
47         return _selected? [_selected]: [];
48     }
49 
50     ///
51     override bool handleMouseButton ( MouseButton btn, bool status, vec2 pos )
52     {
53         if ( super.handleMouseButton(btn,status,pos) ) return true;
54 
55         if ( !style.isPointInside( pos ) ) return false;
56         if ( btn == MouseButton.Left && !status ) {
57             openMenu();
58             return true;
59         }
60         return false;
61     }
62 
63     ///
64     override @property const(Cursor) cursor ()
65     {
66         return Cursor.Hand;
67     }
68 
69     ///
70     this ()
71     {
72         super();
73         _menu       = new CustomPopupMenuWidget;
74         _selected   = null;
75         _menuOpened = false;
76 
77         parseColorSetsFromFile!"colorset/selectinput.yaml"( style );
78         style.box.borderWidth = Rect(1.pixel);
79         style.box.paddings    = Rect(1.mm);
80         style.box.margins     = Rect(1.mm);
81     }
82 
83     /// Changes selection.
84     void select ( MenuItemWidget s )
85     {
86         _selected = s.child;
87         _selected.requestLayout();
88     }
89     /// Opens select menu.
90     void openMenu ()
91     {
92         enforce( _context, "WindowContext is null." );
93 
94         auto size = style.box.borderInsideSize;
95         auto late = style.box.borderInsideLeftTop;
96         late.y   += size.y;
97         late     += style.translate;
98 
99         if ( _selected ) _selected.requestLayout();
100 
101         _context.setPopup( _menu );
102         _menu.move( late, vec2( size.x, 0 ) );
103     }
104 
105     /// Adds an itme.
106     void addItem ( MenuItemWidget i )
107     {
108         i.onPress = () {
109             select( i );
110             return true;
111         };
112         _menu.addItem( i );
113     }
114     /// Removes an item.
115     void removeItem ( MenuItemWidget i )
116     {
117         _menu.removeItem( i );
118     }
119     /// Removes all items.
120     void removeAllItems ()
121     {
122         _menu.removeAllItems();
123     }
124 
125     ///
126     override @property bool trackable () { return true; }
127     ///
128     override @property bool focusable () { return true; }
129 }