1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include "bspf.hxx"
19#include "FrameBuffer.hxx"
20#include "FBSurface.hxx"
21#include "Font.hxx"
22#include "ContextMenu.hxx"
23#include "DialogContainer.hxx"
24#include "PopUpWidget.hxx"
25
26// Little down arrow
27static uInt32 down_arrow[8] = {
28 0b100000001,
29 0b110000011,
30 0b111000111,
31 0b011101110,
32 0b001111100,
33 0b000111000,
34 0b000010000,
35 0b000000000
36};
37
38// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
39PopUpWidget::PopUpWidget(GuiObject* boss, const GUI::Font& font,
40 int x, int y, int w, int h, const VariantList& list,
41 const string& label, int labelWidth, int cmd)
42 : Widget(boss, font, x, y - 1, w, h + 2),
43 CommandSender(boss),
44 _label(label),
45 _labelWidth(labelWidth),
46 _changed(false)
47{
48 _flags = Widget::FLAG_ENABLED | Widget::FLAG_RETAIN_FOCUS;
49 _bgcolor = kDlgColor;
50 _bgcolorhi = kDlgColor; // do not highlight the background
51 _textcolor = kTextColor;
52 _textcolorhi = kTextColor; // do not highlight the label
53
54 if(!_label.empty() && _labelWidth == 0)
55 _labelWidth = _font.getStringWidth(_label);
56
57 _w = w + _labelWidth + 23;
58
59 // vertically center the arrows and text
60 myTextY = (_h - _font.getFontHeight()) / 2;
61 myArrowsY = (_h - 8) / 2;
62
63 myMenu = make_unique<ContextMenu>(this, font, list, cmd, w);
64}
65
66// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
67void PopUpWidget::addItems(const VariantList& items)
68{
69 myMenu->addItems(items);
70}
71
72// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
73void PopUpWidget::setSelected(const Variant& tag, const Variant& def)
74{
75 myMenu->setSelected(tag, def);
76}
77
78// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
79void PopUpWidget::setSelectedIndex(int idx, bool changed)
80{
81 _changed = changed;
82 myMenu->setSelectedIndex(idx);
83}
84
85// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
86void PopUpWidget::setSelectedMax(bool changed)
87{
88 _changed = changed;
89 myMenu->setSelectedMax();
90}
91
92// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
93void PopUpWidget::clearSelection()
94{
95 myMenu->clearSelection();
96}
97
98// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
99int PopUpWidget::getSelected() const
100{
101 return myMenu->getSelected();
102}
103
104// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
105const string& PopUpWidget::getSelectedName() const
106{
107 return myMenu->getSelectedName();
108}
109
110// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
111const Variant& PopUpWidget::getSelectedTag() const
112{
113 return myMenu->getSelectedTag();
114}
115
116// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
117void PopUpWidget::handleMouseDown(int x, int y, MouseButton b, int clickCount)
118{
119 if(isEnabled() && !myMenu->isVisible())
120 {
121 // Add menu just underneath parent widget
122 myMenu->show(getAbsX() + _labelWidth, getAbsY() + getHeight(),
123 dialog().surface().dstRect(), myMenu->getSelected());
124 }
125}
126
127// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
128void PopUpWidget::handleMouseWheel(int x, int y, int direction)
129{
130 if(isEnabled() && !myMenu->isVisible())
131 {
132 if(direction < 0)
133 myMenu->sendSelectionUp();
134 else
135 myMenu->sendSelectionDown();
136 }
137}
138
139// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
140void PopUpWidget::handleMouseEntered()
141{
142 setFlags(Widget::FLAG_HILITED);
143 setDirty();
144}
145
146// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
147void PopUpWidget::handleMouseLeft()
148{
149 clearFlags(Widget::FLAG_HILITED);
150 setDirty();
151}
152
153// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
154bool PopUpWidget::handleEvent(Event::Type e)
155{
156 if(!isEnabled())
157 return false;
158
159 switch(e)
160 {
161 case Event::UISelect:
162 handleMouseDown(0, 0, MouseButton::LEFT, 0);
163 return true;
164 case Event::UIUp:
165 case Event::UILeft:
166 case Event::UIPgUp:
167 return myMenu->sendSelectionUp();
168 case Event::UIDown:
169 case Event::UIRight:
170 case Event::UIPgDown:
171 return myMenu->sendSelectionDown();
172 case Event::UIHome:
173 return myMenu->sendSelectionFirst();
174 case Event::UIEnd:
175 return myMenu->sendSelectionLast();
176 default:
177 return false;
178 }
179}
180
181// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
182void PopUpWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
183{
184 // Intercept all events sent through the PromptWidget
185 // They're likely from our ContextMenu, indicating a redraw is required
186 dialog().setDirty();
187
188 // Pass the cmd on to our parent
189 sendCommand(cmd, data, id);
190}
191
192// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
193void PopUpWidget::drawWidget(bool hilite)
194{
195//cerr << "PopUpWidget::drawWidget\n";
196 FBSurface& s = dialog().surface();
197 bool onTop = _boss->dialog().isOnTop();
198
199 int x = _x + _labelWidth;
200 int w = _w - _labelWidth;
201
202 // Draw the label, if any
203 if(_labelWidth > 0)
204 s.drawString(_font, _label, _x, _y + myTextY, _labelWidth,
205 isEnabled() && onTop ? _textcolor : kColor, TextAlign::Left);
206
207 // Draw a thin frame around us.
208 s.frameRect(x, _y, w, _h, isEnabled() && hilite ? kWidColorHi : kColor);
209 s.frameRect(x + w - 16, _y + 1, 15, _h - 2, isEnabled() && hilite ? kWidColorHi : kBGColorLo);
210
211 // Fill the background
212 s.fillRect(x + 1, _y + 1, w - 17, _h - 2, onTop ? _changed ? kDbgChangedColor : kWidColor : kDlgColor);
213 s.fillRect(x + w - 15, _y + 2, 13, _h - 4, onTop ? isEnabled() && hilite ? kWidColor : kBGColorHi : kBGColorLo);
214 // Draw an arrow pointing down at the right end to signal this is a dropdown/popup
215 s.drawBitmap(down_arrow, x + w - 13, _y + myArrowsY + 1,
216 !(isEnabled() && onTop) ? kColor : kTextColor, 9u, 8u);
217
218 // Draw the selected entry, if any
219 const string& name = myMenu->getSelectedName();
220 TextAlign align = (_font.getStringWidth(name) > w-6) ?
221 TextAlign::Right : TextAlign::Left;
222 s.drawString(_font, name, x+2, _y+myTextY, w-6,
223 !(isEnabled() && onTop) ? kColor : kTextColor, align);
224}
225