|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // スクリーンモニタ
9: //
10:
11: #include "wxscreenmonitor.h"
12: #include "wxscrollbar.h"
13: #include "wxtextscreen.h"
14:
15: //#define WINDOW_DEBUG 1
16:
17: #if defined(WINDOW_DEBUG)
18: #define DPRINTF(fmt...) printf(fmt)
19: #else
20: #define DPRINTF(fmt...) /**/
21: #endif
22:
23: //
24: // スクリーンパネル (共通部)
25: //
26:
27: // イベントテーブル
28: wxBEGIN_EVENT_TABLE(WXScreenPanel, inherited)
29: EVT_MOTION(WXScreenPanel::OnMouseMove)
30: EVT_LEAVE_WINDOW(WXScreenPanel::OnMouseMove)
31: EVT_MOUSEWHEEL(WXScreenPanel::OnMouseWheel)
32: wxEND_EVENT_TABLE()
33:
34: // コンストラクタ
35: WXScreenPanel::WXScreenPanel(wxWindow *parent)
36: : inherited(parent)
37: {
38: cursor = wxDefaultPosition;
39: }
40:
41: // 継承側でサイズが確定したところで呼ぶ、コンストラクタの残り。
42: void
43: WXScreenPanel::Ctor(int plane_width_, int plane_height_)
44: {
45: // 画面のネイティブの大きさ。
46: plane_width = plane_width_;
47: plane_height = plane_height_;
48:
49: // まず等倍でセット。
50: scale = 1;
51: SetScale(1);
52: }
53:
54: // デストラクタ
55: WXScreenPanel::~WXScreenPanel()
56: {
57: }
58:
59: // 倍率を設定
60: void
61: WXScreenPanel::SetScale(int new_scale)
62: {
63: int before = scale;
64:
65: scale = new_scale;
66:
67: // 仮想画面の大きさを計算。
68: virtual_width = plane_width * scale;
69: virtual_height = plane_height * scale;
70:
71: // ビューのスケール変換は前後のスケールがいるのでここでやる。
72: view.x = view.x * scale / before;
73: view.y = view.y * scale / before;
74: view.w = view.w * scale / before;
75: view.h = view.h * scale / before;
76:
77: SetMaxSize(wxSize(virtual_width, virtual_height));
78: }
79:
80: // スクロール位置変更通知
81: void
82: WXScreenPanel::OnScroll(wxScrollEvent& event)
83: {
84: int orient = event.GetOrientation();
85: int pos = event.GetPosition();
86:
87: if (orient == wxHORIZONTAL) {
88: view.x = pos;
89: } else {
90: view.y = pos;
91: }
92: }
93:
94: // マウス移動イベント
95: void
96: WXScreenPanel::OnMouseMove(wxMouseEvent& event)
97: {
98: // pt はこのパネル左上からの位置
99: const wxPoint& pt = event.GetPosition();
100:
101: // pt がパネル内の座標なのに Leaving() になることがある。
102: // pt がパネル外の座標なのに Leaving() にならないことがある。
103: if (event.Leaving() == false && GetClientRect().Contains(pt)) {
104: // cursor は(拡大前の)仮想画面上の座標
105: cursor.x = (view.x + pt.x) / scale;
106: cursor.y = (view.y + pt.y) / scale;
107: assertmsg(cursor.x < plane_width, "%d < %d", cursor.x, plane_width);
108: assertmsg(cursor.y < plane_height, "%d < %d", cursor.y, plane_height);
109: } else {
110: cursor = wxDefaultPosition;
111: }
112: }
113:
114: // マウスホイールイベント
115: void
116: WXScreenPanel::OnMouseWheel(wxMouseEvent& event)
117: {
118: // ホイールは1回で ±120 が飛んでくる。
119: // ここでは 64 ドットずつ移動させてみる。
120: int delta = event.GetWheelRotation() / 30 * 16;
121: int y = view.y - delta;
122: if (y < 0) {
123: y = 0;
124: } else {
125: int max_y = virtual_height - view.h;
126: if (y >= max_y) {
127: y = max_y;
128: }
129: }
130: view.y = y;
131:
132: // マウスカーソルは移動してないが view が動いたので cursor 再計算。
133: OnMouseMove(event);
134:
135: // 縦スクロールバーの位置を追従。
136: auto window = dynamic_cast<WXScreenWindow *>(GetParent());
137: window->SetVScrollPosition(view.y);
138: }
139:
140:
141: //
142: // スクリーンモニタウィンドウ (共通部)
143: //
144:
145: // +----------------------------------------------------+
146: // | wxPanel *ctrlpanel |
147: // | (wx製のコントロールパネル、横目一杯まで伸びる) |
148: // +---------------------------+------------------------+
149: // | WXTextScreen *statuspanel | spacer |
150: // | (固定幅のテキスト情報) | (必要なら空き地をおく) |
151: // +---------------------------+-+----------------------+
152: // | WXScreenPanel *viewpanel | WXScrollBar *vscroll |
153: // | (VRAM 表示パネル) | (縦スクロールバー) |
154: // +-----------------------------+----------------------+
155: // | WXScrollBar *hscroll | corner |
156: // | (横スクロールバー) | (空き地) |
157: // +-----------------------------+----------------------+
158:
159: // イベントテーブル
160: wxBEGIN_EVENT_TABLE(WXScreenWindow, inherited)
161: EVT_TIMER(wxID_ANY, WXScreenWindow::OnTimer)
162: wxEND_EVENT_TABLE()
163:
164: // コンストラクタ
165: WXScreenWindow::WXScreenWindow(wxWindow *parent, const wxString& name)
166: : inherited(parent, wxID_ANY, name, DEFAULT_STYLE | wxRESIZE_BORDER)
167: {
168: SetName(name + ".Window");
169:
170: // 上段、コントロールパネル(のまず下敷き)
171: ctrlpanel = new wxPanel(this);
172: ctrlpanel->SetName(name + ".CtrlPanel");
173:
174: // 中段、情報パネルと右側の空き地用。
175: // 情報パネルは継承クラスで。
176: spacer = new WXBitmapPanel(this);
177:
178: // 下段、表示パネルは継承クラスで。
179:
180: // スクロールバー。
181: vscroll = new WXScrollBar(this, wxID_ANY, wxSB_VERTICAL);
182: hscroll = new WXScrollBar(this, wxID_ANY, wxSB_HORIZONTAL);
183:
184: // 右下の空き地。
185: corner = new WXBitmapPanel(this);
186:
187: // 更新タイマーはモニターパネル同様の 30Hz にしておく。
188: timer.SetOwner(this);
189: timer.Start(33);
190:
191: DPRINTF("%s.ctor end\n", __func__);
192: }
193:
194: // コンストラクタの残り。
195: // statuspanel、viewpanel を作成したあとで呼ぶ。
196: void
197: WXScreenWindow::Ctor()
198: {
199: assert(statuspanel);
200: assert(viewpanel);
201:
202: // 自前レイアウト。
203: SetMyLayout();
204:
205: FontChanged();
206:
207: // スクロールバーからの通知イベントは表示パネルへ回す。
208: vscroll->Connect(NONO_EVT_SCROLL,
209: wxScrollEventHandler(WXScreenPanel::OnScroll), NULL, viewpanel);
210: hscroll->Connect(NONO_EVT_SCROLL,
211: wxScrollEventHandler(WXScreenPanel::OnScroll), NULL, viewpanel);
212: }
213:
214: // デストラクタ
215: WXScreenWindow::~WXScreenWindow()
216: {
217: }
218:
219: bool
220: WXScreenWindow::GetMySizeHints(wxSize *newp, wxSize *minp, wxSize *maxp,
221: wxSize *incp)
222: {
223: DPRINTF("Window.%s\n", __func__);
224:
225: // viewpanel の最小サイズは縦横スクロールバーの大きさで決まる。
226: const wxSize& ctrlsize = ctrlpanel->GetSize();
227: const wxSize& ctrlmin = ctrlpanel->GetMinSize();
228: const wxSize& statsize = statuspanel->GetSize();
229: const wxSize& statmin = statuspanel->GetMinSize();
230: const wxSize& viewsize = viewpanel->GetSize();
231: const wxSize& vscrsize = vscroll->GetSize();
232: const wxSize& vscrmin = vscroll->GetMinSize();
233: const wxSize& hscrsize = hscroll->GetSize();
234:
235: // コントロールからウィンドウサイズを計算。
236: int size_x = std::max(ctrlmin.x, statmin.x);
237: size_x = std::max(size_x, viewsize.x + vscrsize.x);
238: int size_y = ctrlsize.y + statsize.y + viewsize.y + hscrsize.y;
239: wxSize newsize(size_x, size_y);
240:
241: // 最小サイズを計算。
242: int min_x = std::max(ctrlmin.x, statmin.x);
243: // statmin.x は viewpanel の最小幅より必ず大きいのでこれは不要。
244: //min_x = std::max(min_x, hscrmin.x + vscrsize.x);
245: int min_y = ctrlmin.y + statmin.y + vscrmin.y + hscrsize.y;
246: wxSize minsize(min_x, min_y);
247:
248: // 最大サイズは viewsize に依存しそうだから手抜き。
249: const wxSize& viewmax = viewpanel->GetMaxSize();
250: int max_x = viewmax.x;
251: if (max_x > 0) {
252: max_x += vscrsize.x;
253: }
254: int max_y = viewmax.y;
255: if (max_y > 0) {
256: max_y += ctrlmin.y + statmin.y + hscrsize.y;
257: }
258: wxSize maxsize(max_x, max_y);
259:
260: if (newp) *newp = newsize;
261: if (minp) *minp = minsize;
262: if (maxp) *maxp = maxsize;
263: if (incp) *incp = wxDefaultSize;
264: return true;
265: }
266:
267: bool
268: WXScreenWindow::Layout()
269: {
270: if (MyLayout() == false) {
271: DPRINTF("%s.Layout false\n", __func__);
272: // sizer があるので呼ばないといけない。
273: BaseLayout();
274:
275: // コントロールを再配置。
276: const wxSize client = GetClientSize();
277: const wxSize& ctrlsize = ctrlpanel->GetSize();
278: const wxSize& statsize = statuspanel->GetSize();
279: const wxSize& vscrsize = vscroll->GetSize();
280: const wxSize& hscrsize = hscroll->GetSize();
281: DPRINTF("%s.Layout client=(%d,%d)\n", __func__, client.x, client.y);
282: DPRINTF("%s.Layout vscrsize=(%d,%d) hscrsize=(%d,%d)\n", __func__,
283: vscrsize.x, vscrsize.y, hscrsize.x, hscrsize.y);
284: ctrlpanel->SetSize(0, 0, client.x, ctrlsize.y);
285: statuspanel->SetSize(0, ctrlsize.y, statsize.x, statsize.y);
286: int view_y = ctrlsize.y + statsize.y;
287: int view_w = client.x - vscrsize.x;
288: int view_h = client.y - ctrlsize.y - statsize.y - hscrsize.y;
289: viewpanel->SetSize(0, view_y, view_w, view_h);
290: vscroll->SetSize(view_w, view_y, vscrsize.x, view_h);
291: hscroll->SetSize(0, view_y + view_h, view_w, hscrsize.y);
292: corner->SetSize(view_w, view_y + view_h, hscrsize.x, vscrsize.y);
293: DPRINTF("%s.Layout view=(%d,%d,%d,%d)\n", __func__,
294: viewpanel->GetPosition().x,
295: viewpanel->GetPosition().y,
296: viewpanel->GetSize().x,
297: viewpanel->GetSize().y);
298:
299: // ステータスパネル右に空き地が出来るなら埋める。
300: if (statsize.x < client.x) {
301: spacer->SetSize(statsize.x, ctrlsize.y,
302: client.x - statsize.x, statsize.y);
303: spacer->Show();
304: } else {
305: spacer->Hide();
306: }
307:
308: SetScroll();
309:
310: DPRINTF("%s.Layout false done\n", __func__);
311: }
312:
313: return true;
314: }
315:
316: // 倍率変更イベント
317: void
318: WXScreenWindow::OnScale(wxCommandEvent& event)
319: {
320: int scale_index = event.GetSelection();
321:
322: DoScale(scale_index);
323: }
324:
325: // 倍率変更処理
326: void
327: WXScreenWindow::DoScale(int scale_index)
328: {
329: DPRINTF("%s index=%d\n", __method__, scale_index);
330: int scale_ = 1 << scale_index;
331: viewpanel->SetScale(scale_);
332:
333: SetScroll();
334:
335: wxSize maxsize;
336: GetMySizeHints(NULL, NULL, &maxsize, NULL);
337: SetMaxClientSize(maxsize);
338: }
339:
340: // タイマーイベント
341: void
342: WXScreenWindow::OnTimer(wxTimerEvent& event)
343: {
344: viewpanel->Refresh();
345:
346: // この位置の情報で screen を更新
347: TextScreen& screen = statuspanel->GetScreen();
348: const wxPoint& cursor = viewpanel->GetCursor();
349:
350: // UpdateInfo() は cursor が範囲外の時の描画も行っている。
351: UpdateInfo(screen, cursor.x, cursor.y);
352: statuspanel->Refresh();
353: }
354:
355: // 縦スクロールバーの位置を設置する。
356: // (パネルからマウスホイールイベントで呼ばれる)
357: void
358: WXScreenWindow::SetVScrollPosition(int pos)
359: {
360: vscroll->SetThumbPosition(pos);
361: }
362:
363: // サイズ変更処理。パネルサイズと倍率が変わったら呼ぶこと。
364: // 倍率が変わった場合もスクロールバーの再設定とかがあるため。
365: void
366: WXScreenWindow::SetScroll()
367: {
368: wxSize viewsize = viewpanel->GetSize();
369: int virtual_width = viewpanel->GetVirtualWidth();
370: int virtual_height = viewpanel->GetVirtualHeight();
371: Rect& view = viewpanel->view;
372:
373: DPRINTF("%s: viewsize=(%d,%d)\n", __method__, viewsize.x, viewsize.y);
374:
375: // スクロールバーが右端まで行っている場合は右を固定したように見せる。
376: // ただし全体が見えている場合は除く。
377:
378: if (view.x > 0 &&
379: (view.x + view.w == virtual_width ||
380: view.x + viewsize.x >= virtual_width))
381: {
382: view.x = virtual_width - viewsize.x;
383: if (view.x < 0) {
384: view.x = 0;
385: }
386: }
387: view.w = viewsize.x;
388:
389: // 下端も同様
390: if (view.y > 0 &&
391: (view.y + view.h >= virtual_height ||
392: view.y + viewsize.y >= virtual_height))
393: {
394: view.y = virtual_height - viewsize.y;
395: if (view.y < 0) {
396: view.y = 0;
397: }
398: }
399: view.h = viewsize.y;
400:
401: // スクロールバー(横)を再設定
402: assert(hscroll);
403: int hpos = view.x;
404: int hthumbsize = view.w;
405: int hrange = virtual_width;
406: int hpagesize = view.w;
407: hscroll->SetScrollParam(hpos, hthumbsize, hrange, hpagesize);
408:
409: // スクロールバー(縦)を再設定
410: assert(vscroll);
411: int vpos = view.y;
412: int vthumbsize = view.h;
413: int vrange = virtual_height;
414: int vpagesize = view.h;
415: vscroll->SetScrollParam(vpos, vthumbsize, vrange, vpagesize);
416: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.