Giter Site home page Giter Site logo

Comments (15)

probonopd avatar probonopd commented on September 24, 2024 1

Right, the animation. If we want the animation then having Filer involved in rendering the shadow is a bad idea. We need to find a way for Menu to render the shadow itself. Most likely by using a "dummy window" (m_fakeWidget) which is "behind" the real Menu window.

The m_fakeWidget "dummy window" - how can we get it "behind" the real menu bar?

image

/*
 * Copyright (C) 2020 PandaOS Team.
 *
 * Author:     rekols <[email protected]>
 * Portions:   probono <[email protected]>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mainwindow.h"
#include <QApplication>
#include <QHBoxLayout>
#include <QScreen>
#include <QPainter>
#include <QPainterPath>
#include <QDebug>
#include <QApplication>
#include <QLibraryInfo>
#include <KF5/KWindowSystem/KWindowSystem>

#include <QX11Info>
#include <QScreen>
#include <xcb/xcb.h>
#include <X11/Xlib.h>

MainWindow::MainWindow(QWidget *parent)
    : QFrame(parent),
      m_fakeWidget(new QWidget(nullptr)),
      m_mainPanel(new MainPanel)
{
    this->setObjectName("menuBar");
    // Install the translations built-into Qt itself
    qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
    qApp->installTranslator(&qtTranslator);

    // Install our own translations
    translator1.load("menubar_" + QLocale::system().name(), QCoreApplication::applicationDirPath() + QString("/../share/menubar/translations/")); // probono: FHS-like path relative to main binary
    qApp->installTranslator(&translator1);
    translator2.load("menubar_" + QLocale::system().name(), QCoreApplication::applicationDirPath()); // probono: When qm files are next to the executable ("uninstalled"), useful during development
    qApp->installTranslator(&translator2);

    QHBoxLayout *layout = new QHBoxLayout;
    layout->addSpacing(10);
    layout->addWidget(m_mainPanel);
    layout->addSpacing(10);
    layout->setMargin(0);
    layout->setSpacing(0);
    setLayout(layout);

    // m_fakeWidget->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus | Qt::SplashScreen);
    m_fakeWidget->setWindowFlags(Qt::WindowDoesNotAcceptFocus);
    // m_fakeWidget->setAttribute(Qt::WA_TranslucentBackground);

    // Prevent menubar from becoming faded/translucent if we use a compositing manager
    // that fades/makes translucent inactive windows
    m_mainPanel->setWindowFlags(Qt::WindowDoesNotAcceptFocus);

    setAttribute(Qt::WA_NoSystemBackground, false);
    // setAttribute(Qt::WA_TranslucentBackground);
    setWindowFlags(Qt::FramelessWindowHint);

    KWindowSystem::setOnDesktop(effectiveWinId(), NET::OnAllDesktops);

    // "Indicates a toplevel menu (AKA macmenu).
    // This is a KDE extension to the _NET_WM_WINDOW_TYPE mechanism."
    // Source:
    // https://api.kde.org/frameworks/kwindowsystem/html/classNET.html#a4b3115c0f40e7bc8e38119cc44dd60e0
    // Can be inspected with: xwininfo -wm, it contains "Window type: Kde Net Wm Window Type Topmenu"
    // This should allow e.g., picom to set different settings regarding shadows and transparency
    KWindowSystem::setType(winId(), NET::TopMenu);

    //TODO:
    //Call this when the user sets the primary display via xrandr
    initSize();

    //subscribe to changes on our display like if we change the screen resolution, orientation etc..
    connect(qApp->primaryScreen(), &QScreen::geometryChanged, this, &MainWindow::initSize);
    connect(qApp->primaryScreen(), &QScreen::orientationChanged, this, &MainWindow::initSize);
    connect(qApp->primaryScreen(), &QScreen::virtualGeometryChanged, this, &MainWindow::initSize);
    connect(qApp->primaryScreen(), &QScreen::availableGeometryChanged, this, &MainWindow::initSize);
    connect(qApp->primaryScreen(), &QScreen::logicalDotsPerInchChanged, this, &MainWindow::initSize);
    connect(qApp->primaryScreen(), &QScreen::physicalDotsPerInchChanged, this, &MainWindow::initSize);
    connect(qApp->primaryScreen(), &QScreen::physicalSizeChanged, this, &MainWindow::initSize);
    connect(qApp->primaryScreen(), &QScreen::primaryOrientationChanged, this, &MainWindow::initSize);
   
    // Appear with an animation
    QPropertyAnimation *animation = new QPropertyAnimation(this, "pos");
    animation->setDuration(1500);
    animation->setStartValue(QPoint(qApp->primaryScreen()->geometry().x(), -2 * qApp->primaryScreen()->geometry().height()));
    animation->setEndValue(QPoint(qApp->primaryScreen()->geometry().x(),qApp->primaryScreen()->geometry().y()));
    animation->setEasingCurve(QEasingCurve::OutCubic);
    animation->start(QPropertyAnimation::DeleteWhenStopped);
    this->activateWindow(); // probono: Ensure that we have the focus when menu is launched so that one can enter text in the search box
    m_mainPanel->raise(); // probono: Trying to give typing focus to the search box that is in there. Needed? Does not seem tp hurt

}

MainWindow::~MainWindow()
{

}

void MainWindow::paintEvent(QPaintEvent *e)
{

    // probono: Draw black rounded corners on the top edges
    QPainter p(this);
    p.setRenderHint(QPainter::Antialiasing);
    p.setPen(Qt::NoPen);
    int round_pixels = 5; // like /usr/local/etc/xdg/picom.conf // probono: Make this relative to the height of the MainWindow?
    // QPainterPath::subtracted() takes InnerPath and subtracts it from OuterPath to produce the final shape
    QPainterPath OuterPath;
    OuterPath.addRect(0, 0, qApp->primaryScreen()->geometry().width(), 2*round_pixels);
    QPainterPath InnerPath;
    InnerPath.addRoundedRect(QRect(0, 0, qApp->primaryScreen()->geometry().width(), 4*round_pixels), round_pixels, round_pixels);
    QPainterPath FillPath;
    FillPath = OuterPath.subtracted(InnerPath);
    p.fillPath(FillPath, Qt::black);

    // Draw the other widgets
    QWidget::paintEvent(e);
}

void MainWindow::initSize()
{
    QRect primaryRect = qApp->primaryScreen()->geometry();

    setFixedWidth(primaryRect.width());

    // probono: Construct a populated(!) QMenuBar so that we can determine
    // its height and use the same height for the MainWindow. Is there a better way?
    QMenuBar *dummyMenuBar = new QMenuBar;
    dummyMenuBar->setContentsMargins(0, 0, 0, 0);
    dummyMenuBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
    QMenu *dummyMenu = new QMenu;
    dummyMenuBar->addMenu(dummyMenu);
    qDebug() << "probono: dummyMenu->sizeHint().height():" << dummyMenu->sizeHint().height();
    setFixedHeight(dummyMenuBar->sizeHint().height());

    //move this to the active screen and xrandr position
    move(qApp->primaryScreen()->geometry().x(), qApp->primaryScreen()->geometry().y());

    setStrutPartial();
    
    KWindowSystem::setState(winId(), NET::SkipTaskbar); // Do not show in Dock
    KWindowSystem::setState(winId(), NET::StaysOnTop);
    KWindowSystem::setState(winId(), NET::SkipPager);
    KWindowSystem::setState(winId(), NET::SkipSwitcher);
    // How can we set _NET_WM_STATE_ABOVE? KDE krunner has it set

    // https://stackoverflow.com/a/27964691
    // "window should be of type _NET_WM_TYPE_DOCK and you must first map it then move it
    // to position, otherwise the WM may sometimes place it outside of it own strut."
    // _NET_WM_WINDOW_TYPE_DOCK
    KWindowSystem::setType(winId(), NET::Dock);

    // probono: Set background gradient
    // Commenting this out because possibly this interferes with theming via a QSS file via QtPlugin?
    // this->setStyleSheet( "MainWindow { background-color: QLinearGradient( x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #fff, stop: 0.1 #eee, stop: 0.39 #eee, stop: 0.4 #ddd, stop: 1 #eee); }");
}

void MainWindow::setStrutPartial()
{
    //不清真的作法,kwin设置blur后设置程序支撑导致模糊无效
    //TRANSLATED Unclear practice, setting program support after kwin set blur causes blur invalid
    QRect r(geometry());
    m_fakeWidget->setFixedHeight(this->height());
    m_fakeWidget->setFixedWidth(this->width());
    m_fakeWidget->move(10,10);
    // m_fakeWidget->setGeometry(r);
    m_fakeWidget->setVisible(true);

    const QRect windowRect = this->rect();
    NETExtendedStrut strut;

    strut.top_width = height(); // + 1; // 1 pixel between menu bar and maximized window not needed if we have a shadow
    strut.top_start = x();
    strut.top_end = x() + width();

    KWindowSystem::setExtendedStrut(m_fakeWidget->winId(),
                                     strut.left_width,
                                     strut.left_start,
                                     strut.left_end,
                                     strut.right_width,
                                     strut.right_start,
                                     strut.right_end,
                                     strut.top_width,
                                     strut.top_start,
                                     strut.top_end,
                                     strut.bottom_width,
                                     strut.bottom_start,
                                     strut.bottom_end);
}

from filer.

louies0623 avatar louies0623 commented on September 24, 2024

I think you only need to split the Menu Bar and Options menu. This should solve the problem.
Idea menu bar

from filer.

probonopd avatar probonopd commented on September 24, 2024

Thinking a bit more about it, Filer could probably render the shadow as part of when it renders the wallpaper.

from filer.

louies0623 avatar louies0623 commented on September 24, 2024

Can you let Filter render the wallpaper to temporarily hide the menu bar while rendering the shadow, and then move the Menu bar from top to bottom with special effects?

from filer.

louies0623 avatar louies0623 commented on September 24, 2024

Just like this, it can temporarily hide the problem when the Menu bar renders the shadow.
https://user-images.githubusercontent.com/44593430/136714815-940a0114-5a6e-49fb-a6b2-d5148966a7c2.mp4

from filer.

louies0623 avatar louies0623 commented on September 24, 2024

It should be "forced to be ranked first" like a right-click menu or a sticky note.
On the Menubar Menu.

from filer.

kettle-7 avatar kettle-7 commented on September 24, 2024

So would always on top be better or does kwin do that with _NET_WM_DOCK?

from filer.

probonopd avatar probonopd commented on September 24, 2024

The real menu is always on top. But we don't want the shadow to be rendered by that window, otherwise it would be rendered over windows that are close to the menu. Hence, we want another (dummy) window behind the real window, and that should always be at the bottom and have the shadow.

image

from filer.

louies0623 avatar louies0623 commented on September 24, 2024

If the shadow is a transparent gradient image under the window layer instead of being generated by drawing, I think this will save the memory usage of global menu shadow generation.

But pay attention to this tip about drawing.
efgs

from filer.

probonopd avatar probonopd commented on September 24, 2024

The solution is quite simple if you think about it.

Let the code that draws the desktop also draw the shadow for the menu.
Since per the Human Interface Guidelines the menu must never go away (except for an application being in fullscreen, in which case you won't see the desktop), this is a perfectly acceptable solution.

Code along the lines of (not quite working yet but you should get the idea):

#include <QGraphicsEffect>

  QRect shadowRect(QPoint(0, 22), QSize(1600, 10)); // TODO: Screen width instead of 1600
  QLinearGradient alphaGradient(shadowRect.topLeft(), shadowRect.bottomLeft());
  alphaGradient.setColorAt(0.0, Qt::black);
  alphaGradient.setColorAt(1.0, Qt::transparent);
  QGraphicsOpacityEffect *effect = new QGraphicsOpacityEffect;
  effect->setOpacityMask(alphaGradient);
  painter->fillRect(menuRect,  effect);

But where to put it in?

desktopitemdelegate.cpp has a ::paint method where we can put it in, desktopwindow.cpp hasn't.

The following is obviously not the correct way to do it but it is a working except for color gradients instead of transparency gradients being used.

menu_shadow

void DesktopItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
  Q_ASSERT(index.isValid());
  QStyleOptionViewItem opt = option;
  initStyleOption(&opt, index);

  QPen origPen = painter->pen();
  QRect menuRect(QPoint(0, 22), QSize(1600, 10));
  QLinearGradient m_gradient(0,22,0,32);
  m_gradient.setColorAt(0.0, Qt::darkGray);
  m_gradient.setColorAt(1.0, Qt::white);
  painter->fillRect(menuRect,  m_gradient);
  painter->setPen(origPen);
  painter->save();
  painter->setClipRect(option.rect);

Who knows enough about Qt to help get it done?

from filer.

probonopd avatar probonopd commented on September 24, 2024

Now using the proper transparency gradient. Actually easy, just define a QColor with R, G, B, Alpha.

Adding the following to desktopwindow.cpp:

void DesktopWindow::paintEvent(QPaintEvent *)
{
    // This gets drawn but BEHIND the wallpaper. FIXME: Draw above wallpaper
    qDebug() << Q_FUNC_INFO;
    QPainter painter(this);
    QPen origPen = painter.pen();
    QRect menuRect(QPoint(0, 22), QSize(1600, 30));
    QLinearGradient m_gradient(0,22,0,52);
    m_gradient.setColorAt(0.0, QColor(128, 128, 128, 255));
    m_gradient.setColorAt(1.0, QColor(128, 128, 128, 0));
    painter.fillRect(menuRect,  m_gradient);
    painter.setPen(origPen);
    painter.save();
}

The object gets drawn, but behind the wallpaper. Hence it is only visible if the wallpaper is set to transparent.

image

We need to do this differently.

Maybe draw at the end of DesktopWindow::updateWallpaper()? Doesnt seem to work...

Same if I put it into void FolderView::paintEvent(QPaintEvent *) of folderview.cpp.

from filer.

probonopd avatar probonopd commented on September 24, 2024

Maybe we need to create a pixmap that contains the wallpaper and the shadow, and use the combined pixmap instead of just the wallpaper image.

from filer.

probonopd avatar probonopd commented on September 24, 2024

As a temporary workaround, using a wallpaper image that has the shadow "built in":

https://github.com/helloSystem/ISO/releases/download/assets/graphite_shadow.jpg

Kinda crude but effective.

Let's not forget to undo that workaround once we found a proper solution for drawing the shadow using Qt.

from filer.

probonopd avatar probonopd commented on September 24, 2024

This is it:

  QPen origPen = painter->pen();
  QRect shadowRect(QPoint(0, 0), QSize(1600, 33));
  QLinearGradient linearGradient(0,0,0,33);
  linearGradient.setColorAt(0.00, QColor::fromRgbF(0, 0, 0, 0.3));
  linearGradient.setColorAt(0.33, QColor::fromRgbF(0, 0, 0, 0.2));
  linearGradient.setColorAt(1.00, QColor::fromRgbF(0, 0, 0, 0.0));
  painter->fillRect(shadowRect,  linearGradient);
  painter->setPen(origPen);
  painter->save();
  painter->setClipRect(option.rect);

The only remaining question: Where to put this.

from filer.

louies0623 avatar louies0623 commented on September 24, 2024

@probonopd I recently found how to solve the problem of shadow coverage, Ubuntu Unity also has this problem, but it is well adjusted, that is, to reduce the spread of Kwin shadows.
This also reduces system power consumption in window shadow generation.

VirtualBox_PC-1_25_12_2022_19_42_01

from filer.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.