|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// スクリーンモニタ
//
#include "wxscreenmonitor.h"
#include "wxscrollbar.h"
#include "wxtextscreen.h"
//#define WINDOW_DEBUG 1
#if defined(WINDOW_DEBUG)
#define DPRINTF(fmt...) printf(fmt)
#else
#define DPRINTF(fmt...) /**/
#endif
//
// スクリーンパネル (共通部)
//
// イベントテーブル
wxBEGIN_EVENT_TABLE(WXScreenPanel, inherited)
EVT_MOTION(WXScreenPanel::OnMouseMove)
EVT_LEAVE_WINDOW(WXScreenPanel::OnMouseMove)
EVT_MOUSEWHEEL(WXScreenPanel::OnMouseWheel)
wxEND_EVENT_TABLE()
// コンストラクタ
WXScreenPanel::WXScreenPanel(wxWindow *parent)
: inherited(parent)
{
cursor = wxDefaultPosition;
}
// 継承側でサイズが確定したところで呼ぶ、コンストラクタの残り。
void
WXScreenPanel::Ctor(int plane_width_, int plane_height_)
{
// 画面のネイティブの大きさ。
plane_width = plane_width_;
plane_height = plane_height_;
// まず等倍でセット。
scale = 1;
SetScale(1);
}
// デストラクタ
WXScreenPanel::~WXScreenPanel()
{
}
// 倍率を設定
void
WXScreenPanel::SetScale(int new_scale)
{
int before = scale;
scale = new_scale;
// 仮想画面の大きさを計算。
virtual_width = plane_width * scale;
virtual_height = plane_height * scale;
// ビューのスケール変換は前後のスケールがいるのでここでやる。
view.x = view.x * scale / before;
view.y = view.y * scale / before;
view.w = view.w * scale / before;
view.h = view.h * scale / before;
SetMaxSize(wxSize(virtual_width, virtual_height));
}
// スクロール位置変更通知
void
WXScreenPanel::OnScroll(wxScrollEvent& event)
{
int orient = event.GetOrientation();
int pos = event.GetPosition();
if (orient == wxHORIZONTAL) {
view.x = pos;
} else {
view.y = pos;
}
}
// マウス移動イベント
void
WXScreenPanel::OnMouseMove(wxMouseEvent& event)
{
// pt はこのパネル左上からの位置
const wxPoint& pt = event.GetPosition();
// pt がパネル内の座標なのに Leaving() になることがある。
// pt がパネル外の座標なのに Leaving() にならないことがある。
if (event.Leaving() == false && GetClientRect().Contains(pt)) {
// cursor は(拡大前の)仮想画面上の座標
cursor.x = (view.x + pt.x) / scale;
cursor.y = (view.y + pt.y) / scale;
assertmsg(cursor.x < plane_width, "%d < %d", cursor.x, plane_width);
assertmsg(cursor.y < plane_height, "%d < %d", cursor.y, plane_height);
} else {
cursor = wxDefaultPosition;
}
}
// マウスホイールイベント
void
WXScreenPanel::OnMouseWheel(wxMouseEvent& event)
{
// ホイールは1回で ±120 が飛んでくる。
// ここでは 64 ドットずつ移動させてみる。
int delta = event.GetWheelRotation() / 30 * 16;
int y = view.y - delta;
if (y < 0) {
y = 0;
} else {
int max_y = virtual_height - view.h;
if (y >= max_y) {
y = max_y;
}
}
view.y = y;
// マウスカーソルは移動してないが view が動いたので cursor 再計算。
OnMouseMove(event);
// 縦スクロールバーの位置を追従。
auto window = dynamic_cast<WXScreenWindow *>(GetParent());
window->SetVScrollPosition(view.y);
}
//
// スクリーンモニタウィンドウ (共通部)
//
// +----------------------------------------------------+
// | wxPanel *ctrlpanel |
// | (wx製のコントロールパネル、横目一杯まで伸びる) |
// +---------------------------+------------------------+
// | WXTextScreen *statuspanel | spacer |
// | (固定幅のテキスト情報) | (必要なら空き地をおく) |
// +---------------------------+-+----------------------+
// | WXScreenPanel *viewpanel | WXScrollBar *vscroll |
// | (VRAM 表示パネル) | (縦スクロールバー) |
// +-----------------------------+----------------------+
// | WXScrollBar *hscroll | corner |
// | (横スクロールバー) | (空き地) |
// +-----------------------------+----------------------+
// イベントテーブル
wxBEGIN_EVENT_TABLE(WXScreenWindow, inherited)
EVT_TIMER(wxID_ANY, WXScreenWindow::OnTimer)
wxEND_EVENT_TABLE()
// コンストラクタ
WXScreenWindow::WXScreenWindow(wxWindow *parent, const wxString& name)
: inherited(parent, wxID_ANY, name, DEFAULT_STYLE | wxRESIZE_BORDER)
{
SetName(name + ".Window");
// 上段、コントロールパネル(のまず下敷き)
ctrlpanel = new wxPanel(this);
ctrlpanel->SetName(name + ".CtrlPanel");
// 中段、情報パネルと右側の空き地用。
// 情報パネルは継承クラスで。
spacer = new WXBitmapPanel(this);
// 下段、表示パネルは継承クラスで。
// スクロールバー。
vscroll = new WXScrollBar(this, wxID_ANY, wxSB_VERTICAL);
hscroll = new WXScrollBar(this, wxID_ANY, wxSB_HORIZONTAL);
// 右下の空き地。
corner = new WXBitmapPanel(this);
// 更新タイマーはモニターパネル同様の 30Hz にしておく。
timer.SetOwner(this);
timer.Start(33);
DPRINTF("%s.ctor end\n", __func__);
}
// コンストラクタの残り。
// statuspanel、viewpanel を作成したあとで呼ぶ。
void
WXScreenWindow::Ctor()
{
assert(statuspanel);
assert(viewpanel);
// 自前レイアウト。
SetMyLayout();
FontChanged();
// スクロールバーからの通知イベントは表示パネルへ回す。
vscroll->Bind(NONO_EVT_SCROLL, &WXScreenPanel::OnScroll, viewpanel);
hscroll->Bind(NONO_EVT_SCROLL, &WXScreenPanel::OnScroll, viewpanel);
}
// デストラクタ
WXScreenWindow::~WXScreenWindow()
{
}
bool
WXScreenWindow::GetMySizeHints(wxSize *newp, wxSize *minp, wxSize *maxp,
wxSize *incp)
{
DPRINTF("Window.%s\n", __func__);
// viewpanel の最小サイズは縦横スクロールバーの大きさで決まる。
const wxSize& ctrlsize = ctrlpanel->GetSize();
const wxSize& ctrlmin = ctrlpanel->GetMinSize();
const wxSize& statsize = statuspanel->GetSize();
const wxSize& statmin = statuspanel->GetMinSize();
const wxSize& viewsize = viewpanel->GetSize();
const wxSize& vscrsize = vscroll->GetSize();
const wxSize& vscrmin = vscroll->GetMinSize();
const wxSize& hscrsize = hscroll->GetSize();
// コントロールからウィンドウサイズを計算。
int size_x = std::max(ctrlmin.x, statmin.x);
size_x = std::max(size_x, viewsize.x + vscrsize.x);
int size_y = ctrlsize.y + statsize.y + viewsize.y + hscrsize.y;
wxSize newsize(size_x, size_y);
// 最小サイズを計算。
int min_x = std::max(ctrlmin.x, statmin.x);
// statmin.x は viewpanel の最小幅より必ず大きいのでこれは不要。
//min_x = std::max(min_x, hscrmin.x + vscrsize.x);
int min_y = ctrlmin.y + statmin.y + vscrmin.y + hscrsize.y;
wxSize minsize(min_x, min_y);
// 最大サイズは viewsize に依存しそうだから手抜き。
const wxSize& viewmax = viewpanel->GetMaxSize();
int max_x = viewmax.x;
if (max_x > 0) {
max_x += vscrsize.x;
}
int max_y = viewmax.y;
if (max_y > 0) {
max_y += ctrlmin.y + statmin.y + hscrsize.y;
}
wxSize maxsize(max_x, max_y);
if (newp) *newp = newsize;
if (minp) *minp = minsize;
if (maxp) *maxp = maxsize;
if (incp) *incp = wxDefaultSize;
return true;
}
bool
WXScreenWindow::Layout()
{
if (MyLayout() == false) {
DPRINTF("%s.Layout false\n", __func__);
// sizer があるので呼ばないといけない。
BaseLayout();
// コントロールを再配置。
const wxSize client = GetClientSize();
const wxSize& ctrlsize = ctrlpanel->GetSize();
const wxSize& statsize = statuspanel->GetSize();
const wxSize& vscrsize = vscroll->GetSize();
const wxSize& hscrsize = hscroll->GetSize();
DPRINTF("%s.Layout client=(%d,%d)\n", __func__, client.x, client.y);
DPRINTF("%s.Layout vscrsize=(%d,%d) hscrsize=(%d,%d)\n", __func__,
vscrsize.x, vscrsize.y, hscrsize.x, hscrsize.y);
ctrlpanel->SetSize(0, 0, client.x, ctrlsize.y);
statuspanel->SetSize(0, ctrlsize.y, statsize.x, statsize.y);
int view_y = ctrlsize.y + statsize.y;
int view_w = client.x - vscrsize.x;
int view_h = client.y - ctrlsize.y - statsize.y - hscrsize.y;
viewpanel->SetSize(0, view_y, view_w, view_h);
vscroll->SetSize(view_w, view_y, vscrsize.x, view_h);
hscroll->SetSize(0, view_y + view_h, view_w, hscrsize.y);
corner->SetSize(view_w, view_y + view_h, hscrsize.x, vscrsize.y);
DPRINTF("%s.Layout view=(%d,%d,%d,%d)\n", __func__,
viewpanel->GetPosition().x,
viewpanel->GetPosition().y,
viewpanel->GetSize().x,
viewpanel->GetSize().y);
// ステータスパネル右に空き地が出来るなら埋める。
if (statsize.x < client.x) {
spacer->SetSize(statsize.x, ctrlsize.y,
client.x - statsize.x, statsize.y);
spacer->Show();
} else {
spacer->Hide();
}
SetScroll();
DPRINTF("%s.Layout false done\n", __func__);
}
return true;
}
// 倍率変更イベント
void
WXScreenWindow::OnScale(wxCommandEvent& event)
{
int scale_index = event.GetSelection();
DoScale(scale_index);
}
// 倍率変更処理
void
WXScreenWindow::DoScale(int scale_index)
{
DPRINTF("%s index=%d\n", __method__, scale_index);
int scale_ = 1 << scale_index;
viewpanel->SetScale(scale_);
SetScroll();
wxSize maxsize;
GetMySizeHints(NULL, NULL, &maxsize, NULL);
SetMaxClientSize(maxsize);
}
// タイマーイベント
void
WXScreenWindow::OnTimer(wxTimerEvent& event)
{
viewpanel->Refresh();
// この位置の情報で screen を更新
TextScreen& screen = statuspanel->GetScreen();
const wxPoint& cursor = viewpanel->GetCursor();
// UpdateInfo() は cursor が範囲外の時の描画も行っている。
UpdateInfo(screen, cursor.x, cursor.y);
statuspanel->Refresh();
}
// 縦スクロールバーの位置を設置する。
// (パネルからマウスホイールイベントで呼ばれる)
void
WXScreenWindow::SetVScrollPosition(int pos)
{
vscroll->SetThumbPosition(pos);
}
// サイズ変更処理。パネルサイズと倍率が変わったら呼ぶこと。
// 倍率が変わった場合もスクロールバーの再設定とかがあるため。
void
WXScreenWindow::SetScroll()
{
wxSize viewsize = viewpanel->GetSize();
int virtual_width = viewpanel->GetVirtualWidth();
int virtual_height = viewpanel->GetVirtualHeight();
Rect& view = viewpanel->view;
DPRINTF("%s: viewsize=(%d,%d)\n", __method__, viewsize.x, viewsize.y);
// スクロールバーが右端まで行っている場合は右を固定したように見せる。
// ただし全体が見えている場合は除く。
if (view.x > 0 &&
(view.x + view.w == virtual_width ||
view.x + viewsize.x >= virtual_width))
{
view.x = virtual_width - viewsize.x;
if (view.x < 0) {
view.x = 0;
}
}
view.w = viewsize.x;
// 下端も同様
if (view.y > 0 &&
(view.y + view.h >= virtual_height ||
view.y + viewsize.y >= virtual_height))
{
view.y = virtual_height - viewsize.y;
if (view.y < 0) {
view.y = 0;
}
}
view.h = viewsize.y;
// スクロールバー(横)を再設定
assert(hscroll);
int hpos = view.x;
int hthumbsize = view.w;
int hrange = virtual_width;
int hpagesize = view.w;
hscroll->SetScrollParam(hpos, hthumbsize, hrange, hpagesize);
// スクロールバー(縦)を再設定
assert(vscroll);
int vpos = view.y;
int vthumbsize = view.h;
int vrange = virtual_height;
int vpagesize = view.h;
vscroll->SetScrollParam(vpos, vthumbsize, vrange, vpagesize);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.