1// LAF Library
2// Copyright (c) 2019-2022 Igara Studio S.A.
3//
4// This file is released under the terms of the MIT license.
5// Read LICENSE.txt for more information.
6
7#include "gfx/hsv.h"
8#include "gfx/rgb.h"
9#include "os/os.h"
10
11#include <algorithm>
12#include <cstdarg>
13#include <cstdio>
14#include <vector>
15
16static std::vector<os::WindowRef> windows;
17
18const char* lines[] = { "A: Switch mouse cursor to Arrow <-> Move",
19 "H: Hide window (or show all windows again)",
20 "",
21 "C: Change window frame to content",
22 "F: Change window content to frame",
23 "W: Change window content to workarea",
24 "",
25 "D: Duplicate window",
26 "",
27 "Q: Close all windows",
28 "ESC: Close this window" };
29
30static void redraw_window(os::Window* window)
31{
32 os::Surface* s = window->surface();
33 os::Paint paint;
34 paint.color(gfx::rgba(0, 0, 0));
35 s->drawRect(window->bounds(), paint);
36
37 paint.color(gfx::rgba(255, 255, 255));
38
39 char buf[256];
40 int y = 12;
41
42 gfx::Rect rc = window->frame();
43 std::sprintf(buf, "Frame = (%d %d %d %d)", rc.x, rc.y, rc.w, rc.h);
44 os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint);
45 y += 12;
46
47 rc = window->contentRect();
48 std::sprintf(buf, "Content Rect = (%d %d %d %d)", rc.x, rc.y, rc.w, rc.h);
49 os::draw_text(s, nullptr, buf, gfx::Point(0, y), &paint);
50 y += 12;
51
52 for (auto line : lines) {
53 y += 12;
54 os::draw_text(s, nullptr, line, gfx::Point(0, y), &paint);
55 }
56
57 paint.style(os::Paint::Style::Stroke);
58 s->drawRect(window->bounds(), paint);
59}
60
61static os::WindowRef add_window(const std::string& title,
62 const os::WindowSpec& spec)
63{
64 os::WindowRef newWindow = os::instance()->makeWindow(spec);
65 newWindow->setCursor(os::NativeCursor::Arrow);
66 newWindow->setTitle(title);
67 windows.emplace_back(newWindow);
68
69 redraw_window(newWindow.get());
70 newWindow->setVisible(true);
71 return newWindow;
72}
73
74static void check_show_all_windows()
75{
76 // If all windows are hidden, show then again
77 auto hidden = std::count_if(windows.begin(), windows.end(),
78 [](os::WindowRef window){
79 return !window->isVisible();
80 });
81 if (hidden == windows.size()) {
82 std::for_each(windows.begin(), windows.end(),
83 [](os::WindowRef window){
84 window->setVisible(true);
85 });
86 }
87}
88
89static void destroy_window(const os::WindowRef& window)
90{
91 auto it = std::find(windows.begin(), windows.end(), window);
92 if (it != windows.end())
93 windows.erase(it);
94
95 check_show_all_windows();
96}
97
98int app_main(int argc, char* argv[])
99{
100 auto system = os::make_system();
101 system->setAppMode(os::AppMode::GUI);
102 system->handleWindowResize = redraw_window;
103
104 // Create four windows for each screen with the bounds of the
105 // workarea.
106 os::ScreenList screens;
107 system->listScreens(screens);
108 char chr = 'A';
109 for (os::ScreenRef& screen : screens) {
110 os::WindowSpec spec;
111 spec.titled(true);
112 spec.position(os::WindowSpec::Position::Frame);
113 spec.frame(screen->workarea());
114 spec.screen(screen);
115
116 gfx::PointF pos[4] = { gfx::PointF(0.0, 0.0),
117 gfx::PointF(0.5, 0.0),
118 gfx::PointF(0.0, 0.5),
119 gfx::PointF(0.5, 0.5) };
120 for (auto& p : pos) {
121 os::WindowSpec s = spec;
122 gfx::Rect frame = s.frame();
123 frame.x += frame.w*p.x;
124 frame.y += frame.h*p.y;
125 frame.w /= 2;
126 frame.h /= 2;
127 s.frame(frame);
128 add_window(std::string(1, chr++), s);
129 }
130 }
131
132 system->finishLaunching();
133 system->activateApp();
134
135 os::EventQueue* queue = system->eventQueue();
136 os::Event ev;
137 while (!windows.empty()) {
138 queue->getEvent(ev);
139
140 switch (ev.type()) {
141
142 case os::Event::CloseApp:
143 windows.clear(); // Close all windows
144 break;
145
146 case os::Event::CloseWindow:
147 destroy_window(ev.window());
148 break;
149
150 case os::Event::ResizeWindow:
151 redraw_window(ev.window().get());
152 ev.window()->invalidate();
153 break;
154
155 case os::Event::KeyDown:
156 switch (ev.scancode()) {
157
158 case os::kKeyQ:
159 windows.clear();
160 break;
161
162 case os::kKeyEsc:
163 destroy_window(ev.window());
164 break;
165
166 // Switch between Arrow/Move cursor in this specific window
167 case os::kKeyA:
168 ev.window()->setCursor(
169 ev.window()->nativeCursor() == os::NativeCursor::Arrow ?
170 os::NativeCursor::Move:
171 os::NativeCursor::Arrow);
172 break;
173
174 case os::kKeyH:
175 ev.window()->setVisible(!ev.window()->isVisible());
176 check_show_all_windows();
177 break;
178
179 // Duplicate window
180 case os::kKeyD: {
181 std::string title = ev.window()->title();
182 os::WindowSpec spec;
183 spec.position(os::WindowSpec::Position::Frame);
184 spec.frame(ev.window()->frame());
185 add_window(title, spec);
186 break;
187 }
188
189 case os::kKeyF:
190 case os::kKeyC:
191 case os::kKeyW: {
192 std::string title = ev.window()->title();
193 os::WindowSpec spec;
194 if (ev.scancode() == os::kKeyF) {
195 spec.position(os::WindowSpec::Position::ContentRect);
196 spec.contentRect(ev.window()->frame());
197 }
198 else if (ev.scancode() == os::kKeyC) {
199 spec.position(os::WindowSpec::Position::Frame);
200 spec.frame(ev.window()->contentRect());
201 }
202 else if (ev.scancode() == os::kKeyW) {
203 spec.position(os::WindowSpec::Position::Frame);
204 spec.frame(ev.window()->screen()->workarea());
205 }
206
207 // TODO add a new os::Window::setSpec() method instead of re-creating window
208 destroy_window(ev.window());
209 add_window(title, spec);
210 break;
211 }
212
213 // With arrow keys we can thest the os::Window::setFrame() function
214 case os::kKeyLeft:
215 case os::kKeyUp:
216 case os::kKeyRight:
217 case os::kKeyDown: {
218 gfx::Rect rc = ev.window()->frame();
219 switch (ev.scancode()) {
220 case os::kKeyLeft: rc.x -= rc.w; break;
221 case os::kKeyUp: rc.y -= rc.h; break;
222 case os::kKeyRight: rc.x += rc.w; break;
223 case os::kKeyDown: rc.y += rc.h; break;
224 }
225 ev.window()->setFrame(rc);
226
227 // Redraw window because so we can show the new position
228 // on it
229 redraw_window(ev.window().get());
230 ev.window()->invalidate();
231 break;
232 }
233
234 }
235 break;
236
237 default:
238 // Do nothing
239 break;
240 }
241 }
242
243 return 0;
244}
245