ShadeWidget を実現する為に必要な技術的な解説
他のアプリ画面に Shade を被せるには、 普通に透明ウインドウを被せるだけでは、 下側のアプリは使えないだけになってしまいます。
ウインドウが透明なだけでなく、 イベントも無視するウインドウとは?
そんなウインドウがあるのでしょうか?
Window Flags に Qt::Window
これが、 入力を透過するオプションになります。
しかしこれを使うのが簡単ではありません。
このオプションは、 ウインドウ表示後に、 Windows Flags を変更しても反応しません。
ウインドウを作成する時に、オプションで切り替える
ShadeWidgt.cppShadeWidget::ShadeWidget(bool mode, QWidget* parent)
: QDialog(parent)
{
// 枠なしウインドウ
Qt::WindowFlags flags = Qt::Dialog | Qt::WindowStaysOnTopHint;
flags |= Qt::FramelessWindowHint;
if(!mode)
flags |= Qt::WindowTransparentForInput;
setWindowFlags(flags);
// マウス許可
setMouseTracking(true);
// リサイズ可能に
setSizeGripEnabled(true);
ウインドウ作成時に Qt::Window
Qt::Window
だから、 動的に Shade
移動とリサイズを親ウインドウと分担、連携する
設定モードの時は、 ウインドウは操作可能で移動 ・ リサイズを可能とします。
しかし、 Shade
ウインドウのタイトルバーが無いので、 移動は親ウインドウで担当して、 リサイズは Shade
リサイズは親に通知して親側でもバー部分をリサイズする必要があります。
ShadeMain.cpp // フレーム枠用のメッセージウインドウ部品
m_shadewdgt = new ShadeWidget(m_resizeImpossible, this);
connect(m_shadewdgt, SIGNAL(ResizeWidget(QSize)), this, SLOT(onResizeWidget(QSize)));
m_shadewdgt->show();
// リサイズはShadeWidget側から通知が来る
void ShadeMain::onResizeWidget(QSize newsize)
{
if(m_shadewdgt){
m_wdgtW = newsize.width();
m_wdgtH = newsize.height() + 22;
if(m_btnMovable)
m_btnMovable->setMinimumWidth(m_wdgtW-240);
m_parentsize = QSize(m_wdgtW, 22);
resize(m_parentsize);
}
}
// タイトルバー移動で ShadeWidgetも移動
void ShadeMain::onMouseMove(QMouseEvent* event)
{
if(isMoving){
QPoint diff = event->pos() - m_pressPos;
QPoint parentpos = this->pos();
QPoint newpos = parentpos + diff;
move(newpos);
m_wdgtX = newpos.x();
m_wdgtY = newpos.y();
// qDebug() << "newpos=" << newpos;
if(m_shadewdgt){
QPoint winpos = m_shadewdgt->pos() + diff;
// qDebug() << "winpos=" << winpos;
m_shadewdgt->move(winpos);
}
m_parentpos = newpos;
}
}
リサイズは Shade
ShadeWidgt.cppvoid ShadeWidget::resizeEvent(QResizeEvent *event)
{
QDialog::resizeEvent(event);
QSize newsize = event->size();
m_msgwinW = newsize.width();
m_msgwinH = newsize.height();
// 透明QDialogは resizeされているが、
// 親Widgetはリサイズされていないので
emit ResizeWidget(newsize);
}
背景透明化と透明度を調整する為に
もちろん下のアプリが見えるように背景を透明化する必要もあります。
set
Widget のみを透明にはできないのです。
だから、 Shade
ShadeWidgt.cpp // 背景透明化
setWindowOpacity(cfg->m_opacity);
そして透明化は親ウインドウで操作したら、 Shade
ShadeMain.cpp m_opacitySlider = new QSlider(Qt::Horizontal, this);
m_opacitySlider->setFixedHeight(20);
m_opacitySlider->setMinimumWidth(100);
m_opacitySlider->setToolTip(QString::fromUtf8("透明度¥nスライダーを動かして調整"));
m_opacitySlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
m_opacitySlider->setMaximum(90);
m_opacitySlider->setMinimum(30);
m_opacitySlider->setFocusPolicy(Qt::NoFocus);
int opaval = (int)(cfg->m_opacity*100.0F);
m_opacitySlider->setValue(opaval);
SetOpacity(opaval);
// 直接は接続しない
connect(m_opacitySlider, &QSlider::valueChanged, this, &ShadeMain::SetOpacity);
layout->addWidget(m_opacitySlider);
void ShadeMain::SetOpacity(int val)
{
Config *cfg = Config::instance();
double opacity = val / 100.0F;
if(m_shadewdgt){
m_shadewdgt->onSetOpacity(opacity);
cfg->m_opacity = opacity;
}
}
Shade
ShadeWidgt.cpp// 親ウインドウで変更された透明度に変更する
void ShadeWidget::onSetOpacity(double opacity)
{
setWindowOpacity(opacity);
}
最後に
どうだったでしょう。とても沢山の変則的な事を行わないと実現できないのです。
簡単に実現できそうな気がして作り始めましたが、 とても奥深いプログラミングでした。
出来ない事だらけで諦めかけましたが、 なんとか解決策を見つけて実現できたので、 ブログに残そうと考えました。
同じ様な機能を実現したい人の参考になればと思います。ソースを公開して答え合わせをするのもいいですが、 難しい部分はほとんど説明しましたので、 後は研究してみてください。楽しいですよ。