Initial commit

This commit is contained in:
zJes 2015-09-25 10:36:20 +02:00
commit a803329b32
17 changed files with 662 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build
CMakeLists.txt.*

34
CMakeLists.txt Normal file
View File

@ -0,0 +1,34 @@
cmake_minimum_required(VERSION 2.8.11)
cmake_policy(VERSION 2.8)
project(lxqt-appswitcher)
find_package(Qt5 COMPONENTS Core Widgets)
find_package(lxqt)
find_package(lxqt-globalkeys REQUIRED)
find_package(KF5WindowSystem)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
add_definitions("-g -fPIC -Wall -Wextra -pedantic -Wformat=2 -Wnon-virtual-dtor -std=c++11 -Wl,--as-needed -z relro -z defs")
add_executable(${PROJECT_NAME}
src/app-switcher.cpp
src/app-switcher.h
src/app-model.cpp
src/app-model.h
src/app-itemdelegate.cpp
src/app-itemdelegate.h
src/settings.h
src/main.cpp
)
target_link_libraries(${PROJECT_NAME}
Qt5::Core
Qt5::Widgets
lxqt
lxqt-globalkeys
)
add_subdirectory(config)

17
config/CMakeLists.txt Normal file
View File

@ -0,0 +1,17 @@
qt5_wrap_ui(uic
config.ui
)
add_executable(lxqt-config-appswitcher
main.cpp
config.h
config.cpp
${uic}
)
target_link_libraries(lxqt-config-appswitcher
Qt5::Core
Qt5::Widgets
lxqt
)

42
config/config.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "config.h"
#include "ui_config.h"
#include "../src/settings.h"
Config::Config(QWidget *parent) :
QDialog(parent),
m_ui(new Ui::Config)
{
m_ui->setupUi(this);
Settings & set = Settings::instance();
m_ui->iconSizeEdt->setValue(set.iconSize());
m_ui->maxItemsEdt->setValue(set.maxDisplayApps());
m_ui->maxLengthEdt->setValue(set.maxTextWidth());
m_ui->filterDskChk->setChecked(set.filterCurrentDesktop());
m_ui->filterScrChk->setChecked(set.filterCurrentScreen());
connect(m_ui->iconSizeEdt, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this](int){ save(); });
connect(m_ui->maxItemsEdt, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this](int){ save(); });
connect(m_ui->maxLengthEdt, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this](int){ save(); });
connect(m_ui->filterDskChk, &QCheckBox::stateChanged, this, &Config::save);
connect(m_ui->filterScrChk, &QCheckBox::stateChanged, this, &Config::save);
}
Config::~Config()
{
delete m_ui;
}
void Config::save()
{
Settings & set = Settings::instance();
set.setIconSize(m_ui->iconSizeEdt->value());
set.setDisplayApps(m_ui->maxItemsEdt->value());
set.setMaxTextWidth(m_ui->maxLengthEdt->value());
set.setFilterCurrentDesktop(m_ui->filterDskChk->isChecked());
set.setFilterCurrentScreen(m_ui->filterScrChk->isChecked());
set.sync();
}

19
config/config.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <QDialog>
namespace Ui
{ class Config; }
class Config : public QDialog
{
Q_OBJECT
public:
explicit Config(QWidget *parent = 0);
virtual ~Config();
private:
void save();
private:
Ui::Config *m_ui;
};

139
config/config.ui Normal file
View File

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Config</class>
<widget class="QDialog" name="Config">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Appearance:</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Icons size</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="iconSizeEdt"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Max items to show</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="maxItemsEdt"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Max text length</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="maxLengthEdt"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Filters:</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="filterDskChk">
<property name="text">
<string>Filter by desktop</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="filterScrChk">
<property name="text">
<string>Filter by screen</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Config</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Config</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

10
config/main.cpp Normal file
View File

@ -0,0 +1,10 @@
#include <LXQt/Application>
#include "config.h"
int main(int argc, char *argv[])
{
LXQt::Application app(argc, argv);
Config conf;
conf.show();
return app.exec();
}

View File

@ -0,0 +1,9 @@
[Desktop Entry]
Type=Application
Name=Application switcher
Comment=Configure application switcher
Exec=lxqt-config-appswitcher
Icon=input-keyboard
Categories=Qt;Settings;HardwareSettings;DesktopSettings;LXQt;
OnlyShowIn=LXQt;

View File

@ -0,0 +1,19 @@
AppSwitcher {
background: #3C3B37;
border-width: 1px;
border-color: #5e5e5e;
border-style: outset;
color: #FFFFFF;
padding: 5px;
/*border-radius: 5px;*/
margin: 0px;
}
AppSwitcher::item:selected {
border-width: 1px;
border-color: #5e5e5e;
border-style: solid;
border-radius: 5px;
background: #2C2B27;
}

51
src/app-itemdelegate.cpp Normal file
View File

@ -0,0 +1,51 @@
#include <QDebug>
#include <QPainter>
#include <QApplication>
#include "app-model.h"
#include "app-itemdelegate.h"
#include "settings.h"
AppItemDelegate::AppItemDelegate(QObject *parent):
QStyledItemDelegate(parent)
{
}
void AppItemDelegate::init()
{
m_iconSize = Settings::instance().iconSize();
m_maxTextWidth = Settings::instance().maxTextWidth();
}
void AppItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const
{
QStyle *style = option.widget ? option.widget->style() : QApplication::style();
style->drawControl(QStyle::CE_ItemViewItem, &option, painter, option.widget);
QPixmap img = index.model()->data(index, AppRole::Icon).value<QPixmap>();
painter->drawPixmap(option.rect.left()+2, option.rect.top()+2, m_iconSize, m_iconSize, img);
QString text = index.model()->data(index, AppRole::Display).toString();
if (text.length() > m_maxTextWidth){
text = text.left(m_maxTextWidth)+"...";
}
painter->drawText(
QRectF(option.rect.left()+m_iconSize+5, option.rect.top()+2, option.rect.width()-m_iconSize-5, m_iconSize),
Qt::AlignLeft | Qt::AlignVCenter | Qt::TextSingleLine,
text
);
}
QSize AppItemDelegate::sizeHint(const QStyleOptionViewItem &/*option*/, const QModelIndex &index) const
{
QFontMetrics m(qApp->font());
QString text = index.model()->data(index, AppRole::Display).toString();
if (text.length() > m_maxTextWidth){
text = text.left(m_maxTextWidth)+"...";
}
return QSize(m_iconSize + 10 + m.width(text), m_iconSize+4);
}

16
src/app-itemdelegate.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <QStyledItemDelegate>
class AppItemDelegate: public QStyledItemDelegate
{
Q_OBJECT
public:
AppItemDelegate(QObject *parent);
void init();
protected:
virtual void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const;
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
private:
int m_iconSize;
int m_maxTextWidth = 0;
};

60
src/app-model.cpp Normal file
View File

@ -0,0 +1,60 @@
#include <QDebug>
#include <QApplication>
#include <QDesktopWidget>
#include <kwindowsystem.h>
#include "app-model.h"
#include "settings.h"
AppModel::AppModel(QObject *parent):
QAbstractListModel(parent)
{
create();
}
void AppModel::create()
{
int iconSize = Settings::instance().iconSize();
bool byDesk = Settings::instance().filterCurrentDesktop();
bool byScreen = Settings::instance().filterCurrentScreen();
for(WId wid: KWindowSystem::stackingOrder()){
KWindowInfo info(wid, NET::WMVisibleName | NET::WMState | NET::XAWMState | NET::WMWindowType);
NET::WindowType type = info.windowType(NET::AllTypesMask);
if((type == NET::Normal || type == NET::Dialog || type == NET::Unknown) && filter(wid, byDesk, byScreen)){
m_list.prepend({wid, info.visibleName(), KWindowSystem::icon(wid, iconSize, iconSize, true)});
}
}
}
int AppModel::rowCount(const QModelIndex &/*parent*/) const
{
return m_list.size();
}
QVariant AppModel::data(const QModelIndex &index, int role) const
{
switch(role){
case AppRole::Display:
return m_list[index.row()].name;
case AppRole::Icon:
return m_list[index.row()].pixmap;
case AppRole::Window:
return m_list[index.row()].windowId;
}
return QVariant();
}
bool AppModel::filter(WId window, bool byDesk, bool byScreen) const
{
KWindowInfo info(window, NET::WMDesktop | NET::WMFrameExtents);
if (byDesk){
if (KWindowSystem::currentDesktop() != info.desktop())
return false;
}
if (byScreen) {
if (!QApplication::desktop()->screenGeometry(QCursor::pos()).intersects(info.frameGeometry()))
return false;
}
return true;
}

34
src/app-model.h Normal file
View File

@ -0,0 +1,34 @@
#pragma once
#include <QPixmap>
#include <QAbstractListModel>
struct AppInfo
{
WId windowId;
QString name;
QPixmap pixmap;
};
namespace AppRole {
enum Role {
Display = Qt::DisplayRole,
Icon = Qt::UserRole + 1,
Window = Qt::UserRole +2
};
}
class AppModel : public QAbstractListModel
{
Q_OBJECT
public:
AppModel(QObject *parent);
protected:
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
private:
void create();
bool filter(WId window, bool byDesk, bool byScreen) const;
private:
QList<AppInfo> m_list;
};

93
src/app-switcher.cpp Normal file
View File

@ -0,0 +1,93 @@
#include <QDebug>
#include <QApplication>
#include <QDesktopWidget>
#include <LXQtGlobalKeys/Action>
#include <LXQtGlobalKeys/Client>
#include <QKeyEvent>
#include <kwindowsystem.h>
#include "app-switcher.h"
#include "app-model.h"
#include "app-itemdelegate.h"
#include "settings.h"
AppSwitcher::AppSwitcher(QWidget *parent):
QListView(parent)
{
setWindowFlags(Qt::Dialog | Qt::WindowStaysOnTopHint | Qt::CustomizeWindowHint);
setObjectName("AppSwitcher");
//QString shortcut = "Control+Next";
QString shortcut = "Alt+Tab";
m_globalShortcut = GlobalKeyShortcut::Client::instance()->addAction(shortcut, "/app_switcher/switch", tr("Switch applications"), this);
connect(m_globalShortcut, &GlobalKeyShortcut::Action::activated, [this]{
if (isVisible())
selectNextItem();
else
showSwitcher();
});
connect(this, &AppSwitcher::activated, [](const QModelIndex& index){
qDebug() << index.row();
});
setItemDelegate(new AppItemDelegate(this));
setContentsMargins(5, 5, 5, 5);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
void AppSwitcher::showSwitcher()
{
Settings::instance().sync();
setModel(new AppModel(this));
dynamic_cast<AppItemDelegate*>(itemDelegate())->init();
m_current = 0;
int w = 0;
int h = 0;
int maxApp = Settings::instance().maxDisplayApps();
for(int i = 0; i < model()->rowCount(); ++i){
QSize siz = itemDelegate()->sizeHint(QStyleOptionViewItem(), model()->index(i, 0));
w = qMax(w, siz.width());
h += siz.height();
if (i > maxApp)
break;
}
resize(w+contentsMargins().left()+contentsMargins().right(), h+contentsMargins().top()+contentsMargins().bottom());
QRect active = QApplication::desktop()->screenGeometry(QCursor::pos());
move(active.left()+active.width()/2 - width() / 2, active.top()+active.height()/2 - height() / 2);
selectNextItem();
show();
}
void AppSwitcher::selectNextItem()
{
if(++m_current >= model()->rowCount())
m_current = 0;
setCurrentIndex(model()->index(m_current, 0));
setFocus();
}
void AppSwitcher::currentChanged(const QModelIndex &/*current*/, const QModelIndex &/*previous*/)
{
//qDebug() << "current" << current.row();
}
void AppSwitcher::keyPressEvent(QKeyEvent *event)
{
QWidget::keyPressEvent(event);
}
void AppSwitcher::keyReleaseEvent(QKeyEvent *event)
{
if (event->modifiers() == 0){
KWindowSystem::activateWindow(model()->data(model()->index(m_current, 0), AppRole::Window).value<WId>());
close();
}
QWidget::keyReleaseEvent(event);
}

25
src/app-switcher.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include <QListWidget>
#include <QVBoxLayout>
#include <X11/Xlib.h>
namespace GlobalKeyShortcut
{ class Action; }
class AppSwitcher: public QListView
{
Q_OBJECT
public:
AppSwitcher(QWidget *parent);
protected:
virtual void currentChanged(const QModelIndex &current, const QModelIndex &previous);
void keyPressEvent(QKeyEvent *event);
void keyReleaseEvent(QKeyEvent *event);
private:
void showSwitcher();
void selectNextItem();
private:
GlobalKeyShortcut::Action *m_globalShortcut;
int m_current = 0;
};

35
src/main.cpp Normal file
View File

@ -0,0 +1,35 @@
#include <QDebug>
#include <QApplication>
#include <QFile>
#include <LXQt/lxqtsettings.h>
#include <LXQt/Application>
#include "src/app-switcher.h"
#include "src/settings.h"
LXQt::LXQtTheme currentTheme()
{
QString themeName = Settings::instance().theme();
for(const LXQt::LXQtTheme & theme: LXQt::LXQtTheme::allThemes()){
if (theme.name() == themeName)
return theme;
}
return LXQt::LXQtTheme::currentTheme();
}
int main(int argc, char *argv[])
{
LXQt::Application app(argc, argv);
app.setQuitOnLastWindowClosed(false);
LXQt::LXQtTheme theme = currentTheme();
qDebug() << theme.name();
if(QFile::exists(theme.path()+"/lxqt-app-switcher.qss")){
app.setStyleSheet( "file:///" + theme.path()+"/lxqt-app-switcher.qss");
}
QWidget hiddenPreviewParent(0, Qt::Tool);
AppSwitcher switcher(&hiddenPreviewParent);
return app.exec();
}

57
src/settings.h Normal file
View File

@ -0,0 +1,57 @@
#pragma once
#include <QSettings>
class Settings
{
public:
static Settings& instance()
{
static Settings sets;
return sets;
}
QString theme() const
{ return m_sets.value("appearance/lxqt-theme", "ambiance").toString(); }
void setTheme(const QString& theme)
{ m_sets.setValue("appearance/lxqt-theme", theme); }
int iconSize() const
{ return m_sets.value("appearance/icon-size", 38).toInt(); }
void setIconSize(int size)
{ m_sets.setValue("appearance/icon-size", size); }
int maxDisplayApps() const
{ return m_sets.value("appearance/max-disp-apps", 15).toInt(); }
void setDisplayApps(int size)
{ m_sets.setValue("appearance/max-disp-apps", size); }
int maxTextWidth() const
{ return m_sets.value("appearance/max-text-width", 50).toInt(); }
void setMaxTextWidth(int width)
{ m_sets.setValue("appearance/max-text-width", width); }
bool filterCurrentDesktop() const
{ return m_sets.value("filter/current-desktop", true).toBool(); }
void setFilterCurrentDesktop(bool filter)
{ m_sets.setValue("filter/current-desktop", filter); }
bool filterCurrentScreen() const
{ return m_sets.value("filter/current-screen", true).toBool(); }
void setFilterCurrentScreen(bool filter)
{ m_sets.setValue("filter/current-screen", filter); }
void sync()
{ m_sets.sync(); }
private:
Settings():
m_sets("lxqt", "app-switcher")
{}
private:
QSettings m_sets;
};