2023-04-16  ShadeWidget

shadewidget の技術的な事1

ShadeWidget を実現する為に必要な技術的な解説

他のアプリ画面に Shade を被せるには 普通に透明ウインドウを被せるだけでは 下側のアプリは使えないだけになってしまいます。

ウインドウが透明なだけでなく イベントも無視するウインドウとは?
そんなウインドウがあるのでしょうか?

Window Flags Qt::WindowTransparentForInput というオプションがあります。
これが 入力を透過するオプションになります。

しかしこれを使うのが簡単ではありません。
このオプションは ウインドウ表示後に Windows Flags を変更しても反応しません。

ウインドウを作成する時に、オプションで切り替える

ShadeWidgt.cpp
ShadeWidget::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::WindowTransparentForInput mode で切り替えるのですが 他のアプリに被せるためにはウインドウを移動 リサイズ可能にする必要もあります。
Qt::WindowTransparentForInput のウインドウは リサイズも不可です。
だから 動的に ShadeWidget を作成して mode で変更する構成にします。

移動とリサイズを親ウインドウと分担、連携する

設定モードの時は ウインドウは操作可能で移動 リサイズを可能とします。
しかし ShadeWidget 側は 枠無しウインドウです。
ウインドウのタイトルバーが無いので 移動は親ウインドウで担当して リサイズは ShadeWidget 側となります。

リサイズは親に通知して親側でもバー部分をリサイズする必要があります。

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; } }

リサイズは ShadeWidget で行われるので emit で親に通知します。

ShadeWidgt.cpp
void 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); }

背景透明化と透明度を調整する為に

もちろん下のアプリが見えるように背景を透明化する必要もあります。
setWindowOpacity はウインドウ全体が透明化します。
Widget のみを透明にはできないのです。
だから ShadeMain ShadeWidget に分割する必要があります。

ShadeWidgt.cpp
// 背景透明化 setWindowOpacity(cfg->m_opacity);

そして透明化は親ウインドウで操作したら ShadeWidget 側で透明度を変更するようにします。

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; } }

ShadeWidget 側では 透明度を設定します。

ShadeWidgt.cpp
// 親ウインドウで変更された透明度に変更する void ShadeWidget::onSetOpacity(double opacity) { setWindowOpacity(opacity); }

最後に

どうだったでしょう。とても沢山の変則的な事を行わないと実現できないのです。
簡単に実現できそうな気がして作り始めましたが とても奥深いプログラミングでした。
出来ない事だらけで諦めかけましたが なんとか解決策を見つけて実現できたので ブログに残そうと考えました。

同じ様な機能を実現したい人の参考になればと思います。ソースを公開して答え合わせをするのもいいですが 難しい部分はほとんど説明しましたので 後は研究してみてください。楽しいですよ。

最終更新日 2023-05-11
この記事を共有しませんか?
ブックマーク