|
|
nono 1.7.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// モニター
//
#include "wxmonitor.h"
#include "wxmainframe.h"
#include "monitor.h"
//#define WINDOW_DEBUG 1
#if defined(WINDOW_DEBUG)
#define DPRINTF(fmt...) printf(fmt)
#else
#define DPRINTF(fmt...) /**/
#endif
//
// モニターパネル
//
// イベントテーブル
wxBEGIN_EVENT_TABLE(WXMonitorPanel, inherited)
EVT_TIMER(wxID_ANY, WXMonitorPanel::OnTimer)
wxEND_EVENT_TABLE()
// コンストラクタ
WXMonitorPanel::WXMonitorPanel(wxWindow *parent, Monitor *monitor_)
: inherited(parent, monitor_->GetSize())
, monitor(monitor_)
{
timer.SetOwner(this);
// 現在の設定値を適用
auto mainframe = dynamic_cast<WXMainFrame*>(GetParent()->GetParent());
SetRate(mainframe->GetMonitorRate());
// 今の所パディングは変わらない。
monitor->SetPadding(WXTextScreen::DefaultPadding);
FontChanged();
// 最初に一回描画を起こす
wxTimerEvent dummy(timer);
AddPendingEvent(dummy);
}
// デストラクタ
WXMonitorPanel::~WXMonitorPanel()
{
}
// フォントサイズ変更
void
WXMonitorPanel::FontChanged()
{
inherited::FontChanged();
monitor->SetFontSize(font_width, font_height);
}
// タイマーイベント
void
WXMonitorPanel::OnTimer(wxTimerEvent& event)
{
DoRefresh();
}
// 画面を更新して再描画する。
//
// 通常はタイマーイベントにより更新間隔ごとに呼ばれるが、
// ユーザ操作により画面の更新が必要ならその都度直接これを呼ぶ。
void
WXMonitorPanel::DoRefresh()
{
bool updated = false;
// モニタースクリーンを更新して
monitor->UpdateScreen(screen);
if (screen.GetBuf() != prevbuf) {
updated = true;
}
// あればビットマップ描画を足して
if (monitor->UpdateBitmap(bitmap)) {
updated = true;
}
// 必要なら再描画
if (updated) {
Refresh();
}
}
// 画面更新頻度を Hz で設定する。
void
WXMonitorPanel::SetRate(int hz)
{
timer.Start(1000 / hz);
}
//
// モニターウィンドウ
//
// コンストラクタ
WXMonitorWindow::WXMonitorWindow(wxWindow *parent, const wxString& name,
Monitor *monitor_)
: inherited(parent, wxID_ANY, name)
{
// モニタパネル
monpanel = new WXMonitorPanel(this, monitor_);
Fit();
}
// デストラクタ
WXMonitorWindow::~WXMonitorWindow()
{
}
//
// 縦スクロールバー付きモニタウィンドウ
//
// コンストラクタ
WXScrollMonitorWindow::WXScrollMonitorWindow(wxWindow *parent,
const wxString& name, Monitor *monitor_)
: inherited(parent, wxID_ANY, name, DEFAULT_STYLE | wxRESIZE_BORDER)
{
DPRINTF("%s begin\n", __method__);
// +--------------+---------+
// | MonitorPanel | VScroll |
// +--------------+---------+
// モニタパネル
monpanel = new WXMonitorPanel(this, monitor_);
// スクロールバー
vscroll = new WXScrollBar(this, wxID_ANY, wxSB_VERTICAL);
// 自前レイアウト。
SetMyLayout();
// ウィンドウサイズを確定させる
FontChanged();
// スクロールのために (パネル領域での) MouseWheel イベントもここで
// 受け取りたい。スクロールバー上のマウスホイール操作はスクロールバーが
// 処理しているので問題ないが、パネル領域でのマウスホイール操作はここ
// (wxFrame)ではなくパネルに対して飛んでくる。
// ここで一緒に処理したほうが楽なので、こちらに回す。
monpanel->Bind(wxEVT_MOUSEWHEEL,
&WXScrollMonitorWindow::OnMouseWheel, this);
// スクロールバーからの位置変更通知
vscroll->Bind(NONO_EVT_SCROLL, &WXScrollMonitorWindow::OnScroll, this);
DPRINTF("%s done\n", __method__);
}
// デストラクタ
WXScrollMonitorWindow::~WXScrollMonitorWindow()
{
}
void
WXScrollMonitorWindow::FontChanged()
{
monpanel->FontChanged();
vscroll->FontChanged();
DPRINTF("%s %d\n", __method__, monpanel->GetFontHeight());
Fit();
}
// 自前レイアウト方式のサイズに関する情報を返す。
bool
WXScrollMonitorWindow::GetMySizeHints(wxSize *newp, wxSize *minp, wxSize *maxp,
wxSize *incp)
{
auto psize = monpanel->GetSize();
auto vsize = vscroll->GetSize();
// 幅は monpanel 幅 + スクロールバーの幅、高さは monpanel の高さで決まる。
// monpanel には四辺に Padding があることに注意。
int x = psize.x + vsize.x;
int min_y = std::max(vscroll->GetMinSize().y, monpanel->GetScreenHeight(1));
int new_y = std::max(psize.y, min_y);
wxSize newsize(x, new_y);
wxSize minsize(x, min_y);
wxSize maxsize(x, GetMaxClientSize().y);
wxSize incsize(0, monpanel->GetFontHeight());
if (newp) *newp = newsize;
if (minp) *minp = minsize;
if (maxp) *maxp = maxsize;
if (incp) *incp = incsize;
return true;
}
bool
WXScrollMonitorWindow::Layout()
{
if (MyLayout() == false) {
// コントロールを配置。
wxSize client = GetClientSize();
const wxSize vsize = vscroll->GetSize();
int mon_width = client.x - vsize.x;
int mon_height = client.y;
// 最低でも 1px ないと GTK とかに怒られる。今は起きないはず?
if (__predict_false(mon_width < 1)) {
mon_width = 1;
}
if (__predict_false(mon_height < 1)) {
mon_height = 1;
}
#if defined(WINDOW_DEBUG)
const wxSize win = GetSize();
const wxSize minwin = GetMinSize();
printf("%s WinSize =(%d,%d) MinWin =(%d,%d) Shown=%u\n",
__method__, win.x, win.y, minwin.x, minwin.y, IsShown());
const wxSize min = GetMinClientSize();
printf("%s ClientSize=(%d,%d) MinClient=(%d,%d)\n",
__method__, client.x, client.y, min.x, min.y);
#endif
monpanel->SetSize(0, 0, mon_width, mon_height);
vscroll->SetSize(mon_width, 0, vsize.x, mon_height);
#if defined(WINDOW_DEBUG)
const wxSize monsize = monpanel->GetSize();
const wxSize vsize1 = vscroll->GetSize();
int py = monsize.y - monpanel->GetPaddingY();
int height = monpanel->GetFontHeight();
int row = (height != 0) ? (py / height) : 0;
int res = (height != 0) ? (py % height) : 0;
printf("%s mon=(%d,%d) vscroll=(%d,%d), row=%d%s\n", __method__,
monsize.x, monsize.y, vsize1.x, vsize1.y,
row, (res == 0 ? "" : "+"));
#endif
// スクロールバーを再設定。
// 今の所ヘッダが常に1行ある。
static int HEADLINES = 1;
int pos = (int)monpanel->GetUserData();
int thumbsize = monpanel->GetRow() - HEADLINES;
int range = monpanel->GetMonitor()->GetMaxHeight() - HEADLINES;
int pagesize = thumbsize - 1;
if (pos > range - thumbsize) {
pos = range - thumbsize;
DoScroll(pos);
}
vscroll->SetScrollParam(pos, thumbsize, range, pagesize);
}
return true;
}
// マウスホイールイベント (WXTextScreen 上で起きたやつをこっちに回してある)
void
WXScrollMonitorWindow::OnMouseWheel(wxMouseEvent& event)
{
// GetWheelRotation() は上(奥)向きに回すと +120、下(手前)に回すと -120。
// どこの決まりか知らんけど、スクロールバーのほうはホイール1段階で3行
// スクロールするのでそれに合わせる。
int pos = (int)monpanel->GetUserData();
pos = pos - event.GetWheelRotation() / 40;
int maxpos = vscroll->GetRange() - vscroll->GetThumbSize();
if (pos < 0)
pos = 0;
if (pos > maxpos)
pos = maxpos;
DoScroll(pos);
// スクロールバーの位置を追従
vscroll->SetThumbPosition(pos);
}
// スクロールバーからの通知イベント
void
WXScrollMonitorWindow::OnScroll(wxScrollEvent& event)
{
DoScroll(event.GetPosition());
}
// スクロール処理 (OnScroll() と OnMouseWheel() から呼ばれる)
void
WXScrollMonitorWindow::DoScroll(int pos)
{
// userdata が表示開始位置になっている
monpanel->SetUserData(pos);
monpanel->DoRefresh();
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.