Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
145 KB
Referenced Files
None
Subscribers
None
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index 86e37e3..22266ab 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -1,2591 +1,2598 @@
#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "AboutDialog.h"
#include "ApplicationGlobal.h"
#include "AreYouSureYouWantToContinueConnectingDialog.h"
#include "AvatarLoader.h"
#include "BlameWindow.h"
#include "CommitPropertyDialog.h"
#include "DeleteBranchDialog.h"
#include "EditGitIgnoreDialog.h"
#include "EditTagsDialog.h"
#include "FileDiffWidget.h"
#include "GitDiff.h"
#include "JumpDialog.h"
#include "LineEditDialog.h"
#include "MySettings.h"
#include "ReflogWindow.h"
#include "RemoteWatcher.h"
#include "SetGpgSigningDialog.h"
#include "SettingsDialog.h"
#include "StatusLabel.h"
#include "TextEditDialog.h"
#include "common/joinpath.h"
#include "common/misc.h"
#include "CloneFromGitHubDialog.h"
#include "ObjectBrowserDialog.h"
#include "platform.h"
#include "webclient.h"
#include <QClipboard>
#include <QDirIterator>
#include <QFileDialog>
#include <QFileIconProvider>
#include <QMessageBox>
#include <QMimeData>
#include <QPainter>
#include <QStandardPaths>
#include <QTimer>
FileDiffWidget::DrawData::DrawData()
{
bgcolor_text = QColor(255, 255, 255);
bgcolor_gray = QColor(224, 224, 224);
bgcolor_add = QColor(192, 240, 192);
bgcolor_del = QColor(255, 224, 224);
bgcolor_add_dark = QColor(64, 192, 64);
bgcolor_del_dark = QColor(240, 64, 64);
}
struct MainWindow::Private {
bool is_online_mode = true;
QTimer interval_10ms_timer;
QTimer remote_watcher_timer;
QImage graph_color;
QPixmap digits;
StatusLabel *status_bar_label;
QObject *last_focused_file_list = nullptr;
QListWidgetItem *last_selected_file_item = nullptr;
RemoteWatcher remote_watcher;
int repos_panel_width = 0;
};
MainWindow::MainWindow(QWidget *parent)
: BasicMainWindow(parent)
, ui(new Ui::MainWindow)
, m(new Private)
{
ui->setupUi(this);
#ifdef Q_OS_MACX
ui->action_about->setText("About Guitar...");
ui->action_edit_settings->setText("Settings...");
#endif
ui->splitter_v->setSizes({100, 400});
ui->splitter_h->setSizes({200, 100, 200});
m->status_bar_label = new StatusLabel(this);
ui->statusBar->addWidget(m->status_bar_label);
ui->widget_diff_view->bind(this);
qApp->installEventFilter(this);
ui->widget_log->setupForLogWidget(ui->verticalScrollBar_log, ui->horizontalScrollBar_log, themeForTextEditor());
onLogVisibilityChanged();
initNetworking();
showFileList(FilesListType::SingleList);
m->digits.load(":/image/digits.png");
m->graph_color = global->theme->graphColorMap();
prepareLogTableWidget();
#ifdef Q_OS_WIN
{
QFont font;
font = ui->label_repo_name->font();
font.setFamily("Meiryo");
ui->label_repo_name->setFont(font);
font = ui->label_branch_name->font();
font.setFamily("Meiryo");
ui->label_branch_name->setFont(font);
}
#endif
connect(this, &BasicMainWindow::signalWriteLog, this, &BasicMainWindow::writeLog_);
connect(ui->dockWidget_log, &QDockWidget::visibilityChanged, this, &MainWindow::onLogVisibilityChanged);
connect(ui->widget_log, &TextEditorWidget::idle, this, &MainWindow::onLogIdle);
connect(ui->treeWidget_repos, &RepositoriesTreeWidget::dropped, this, &MainWindow::onRepositoriesTreeDropped);
connect((AbstractPtyProcess *)getPtyProcess(), &AbstractPtyProcess::completed, this, &MainWindow::onPtyProcessCompleted);
connect(this, &BasicMainWindow::remoteInfoChanged, [&](){
ui->lineEdit_remote->setText(currentRemoteName());
});
// リモート監視
connect(this, &BasicMainWindow::signalCheckRemoteUpdate, &m->remote_watcher, &RemoteWatcher::checkRemoteUpdate);
connect(&m->remote_watcher_timer, &QTimer::timeout, &m->remote_watcher, &RemoteWatcher::checkRemoteUpdate);
connect(this, &MainWindow::updateButton, [&](){
doUpdateButton();
});
m->remote_watcher.start(this);
setRemoteMonitoringEnabled(true);
connect(this, &MainWindow::signalSetRemoteChanged, [&](bool f){
setRemoteChanged(f);
updateButton();
});
//
QString path = getBookmarksFilePath();
*getReposPtr() = RepositoryBookmark::load(path);
updateRepositoriesList();
webContext()->set_keep_alive_enabled(true);
getAvatarLoader()->start(this);
connect(getAvatarLoader(), &AvatarLoader::updated, this, &MainWindow::onAvatarUpdated);
*ptrUpdateFilesListCounter() = 0;
connect(ui->widget_diff_view, &FileDiffWidget::textcodecChanged, [&](){ updateDiffView(); });
if (!global->start_with_shift_key && appsettings()->remember_and_restore_window_position) {
Qt::WindowStates state = windowState();
MySettings settings;
settings.beginGroup("MainWindow");
bool maximized = settings.value("Maximized").toBool();
restoreGeometry(settings.value("Geometry").toByteArray());
// ui->splitter->restoreState(settings.value("SplitterState").toByteArray());
settings.endGroup();
if (maximized) {
state |= Qt::WindowMaximized;
setWindowState(state);
}
}
setTabOrder(ui->treeWidget_repos, ui->widget_log);
// setTabOrder(ui->widget_log, ui->treeWidget_repos);
startTimers();
}
MainWindow::~MainWindow()
{
stopPtyProcess();
getAvatarLoader()->stop();
m->remote_watcher.quit();
m->remote_watcher.wait();
delete m;
delete ui;
}
void MainWindow::notifyRemoteChanged(bool f)
{
postUserFunctionEvent([&](QVariant const &v){
setRemoteChanged(v.toBool());
updateButton();
}, QVariant(f));
}
bool MainWindow::shown()
{
while (!misc::isExecutable(appsettings()->git_command) || !misc::isExecutable(appsettings()->file_command)) {
if (!execWelcomeWizardDialog()) {
return false;
}
}
m->repos_panel_width = ui->stackedWidget_leftpanel->width();
ui->stackedWidget_leftpanel->setCurrentWidget(ui->page_repos);
ui->action_repositories_panel->setChecked(true);
setGitCommand(appsettings()->git_command, false);
setFileCommand(appsettings()->file_command, false);
writeLog(AboutDialog::appVersion() + '\n'); // print application version
logGitVersion(); // print git command version
setGpgCommand(appsettings()->gpg_command, false);
{
MySettings s;
s.beginGroup("Remote");
bool f = s.value("Online", true).toBool();
s.endGroup();
setRemoteOnline(f);
}
setUnknownRepositoryInfo();
checkUser();
return true;
}
void MainWindow::startTimers()
{
// interval 10ms
connect(&m->interval_10ms_timer, &QTimer::timeout, [&](){
const int ms = 10;
auto *p1 = ptrUpdateCommitTableCounter();
if (*p1 > 0) {
if (*p1 > ms) {
*p1 -= ms;
} else {
*p1 = 0;
ui->tableWidget_log->viewport()->update();
}
}
auto *p2 = ptrUpdateFilesListCounter();
if (*p2 > 0) {
if (*p2 > ms) {
*p2 -= ms;
} else {
*p2 = 0;
updateCurrentFilesList();
}
}
});
m->interval_10ms_timer.setInterval(10);
m->interval_10ms_timer.start();
startTimer(10);
}
TextEditorThemePtr BasicMainWindow::themeForTextEditor()
{
return global->theme->text_editor_theme;
}
void MainWindow::setCurrentLogRow(int row)
{
if (row >= 0 && row < ui->tableWidget_log->rowCount()) {
ui->tableWidget_log->setCurrentCell(row, 2);
ui->tableWidget_log->setFocus();
updateStatusBarText();
}
}
bool MainWindow::event(QEvent *event)
{
QEvent::Type et = event->type();
if (et == QEvent::WindowActivate) {
checkRemoteUpdate();
} else if (et == QEvent::KeyPress) {
auto *e = dynamic_cast<QKeyEvent *>(event);
Q_ASSERT(e);
int k = e->key();
if (k == Qt::Key_Escape) {
emit onEscapeKeyPressed();
} else if (k == Qt::Key_Delete) {
if (qApp->focusWidget() == ui->treeWidget_repos) {
removeSelectedRepositoryFromBookmark(true);
return true;
}
}
}
return BasicMainWindow::event(event);
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
QEvent::Type et = event->type();
if (et == QEvent::KeyPress) {
if (QApplication::activeModalWidget()) {
// thru
} else {
auto *e = dynamic_cast<QKeyEvent *>(event);
Q_ASSERT(e);
int k = e->key();
if (k == Qt::Key_Escape) {
if (centralWidget()->isAncestorOf(qApp->focusWidget())) {
ui->treeWidget_repos->setFocus();
return true;
}
}
if (e->modifiers() & Qt::ControlModifier) {
if (k == Qt::Key_Up || k == Qt::Key_Down) {
int rows = ui->tableWidget_log->rowCount();
int row = ui->tableWidget_log->currentRow();
if (k == Qt::Key_Up) {
if (row > 0) {
row--;
}
} else if (k == Qt::Key_Down) {
if (row + 1 < rows) {
row++;
}
}
ui->tableWidget_log->setCurrentCell(row, 0);
return true;
}
}
if (watched == ui->treeWidget_repos) {
if (k == Qt::Key_Enter || k == Qt::Key_Return) {
openSelectedRepository();
return true;
}
if (!(e->modifiers() & Qt::ControlModifier)) {
if (k >= 0 && k < 128 && QChar((uchar)k).isLetterOrNumber()) {
appendCharToRepoFilter(k);
return true;
}
if (k == Qt::Key_Backspace) {
backspaceRepoFilter();
return true;
}
if (k == Qt::Key_Escape) {
clearRepoFilter();
return true;
}
}
} else if (watched == ui->tableWidget_log) {
if (k == Qt::Key_Home) {
setCurrentLogRow(0);
return true;
}
if (k == Qt::Key_Escape) {
ui->treeWidget_repos->setFocus();
return true;
}
} else if (watched == ui->listWidget_files || watched == ui->listWidget_unstaged || watched == ui->listWidget_staged) {
if (k == Qt::Key_Escape) {
ui->tableWidget_log->setFocus();
return true;
}
}
}
} else if (et == QEvent::FocusIn) {
auto SelectItem = [](QListWidget *w){
int row = w->currentRow();
if (row < 0) {
row = 0;
w->setCurrentRow(row);
}
w->setItemSelected(w->item(row), true);
w->viewport()->update();
};
// ファイルリストがフォーカスを得たとき、diffビューを更新する。(コンテキストメニュー対応)
if (watched == ui->listWidget_unstaged) {
m->last_focused_file_list = watched;
updateStatusBarText();
updateUnstagedFileCurrentItem();
SelectItem(ui->listWidget_unstaged);
return true;
}
if (watched == ui->listWidget_staged) {
m->last_focused_file_list = watched;
updateStatusBarText();
updateStagedFileCurrentItem();
SelectItem(ui->listWidget_staged);
return true;
}
if (watched == ui->listWidget_files) {
m->last_focused_file_list = watched;
SelectItem(ui->listWidget_files);
return true;
}
}
return false;
}
void MainWindow::closeEvent(QCloseEvent *event)
{
if (appsettings()->remember_and_restore_window_position) {
setWindowOpacity(0);
Qt::WindowStates state = windowState();
bool maximized = (state & Qt::WindowMaximized) != 0;
if (maximized) {
state &= ~Qt::WindowMaximized;
setWindowState(state);
}
{
MySettings settings;
settings.beginGroup("MainWindow");
settings.setValue("Maximized", maximized);
settings.setValue("Geometry", saveGeometry());
// settings.setValue("SplitterState", ui->splitter->saveState());
settings.endGroup();
}
}
QMainWindow::closeEvent(event);
}
void MainWindow::setStatusBarText(QString const &text)
{
m->status_bar_label->setText(text);
}
void MainWindow::clearStatusBarText()
{
setStatusBarText(QString());
}
QString BasicMainWindow::getObjectID(QListWidgetItem *item)
{
int i = indexOfDiff(item);
if (i >= 0 && i < diffResult()->size()) {
Git::Diff const &diff = diffResult()->at(i);
return diff.blob.a_id;
}
return QString();
}
void MainWindow::onLogVisibilityChanged()
{
ui->action_window_log->setChecked(ui->dockWidget_log->isVisible());
}
void MainWindow::internalWriteLog(char const *ptr, int len)
{
ui->widget_log->logicalMoveToBottom();
ui->widget_log->write(ptr, len, false);
ui->widget_log->setChanged(false);
setInteractionCanceled(false);
}
void BasicMainWindow::writeLog(QString const &str)
{
std::string s = str.toStdString();
writeLog(s.c_str(), s.size());
}
void BasicMainWindow::writeLog_(QByteArray ba)
{
if (!ba.isEmpty()) {
writeLog(ba.data(), ba.size());
}
}
void MainWindow::buildRepoTree(QString const &group, QTreeWidgetItem *item, QList<RepositoryItem> *repos)
{
QString name = item->text(0);
if (isGroupItem(item)) {
int n = item->childCount();
for (int i = 0; i < n; i++) {
QTreeWidgetItem *child = item->child(i);
QString sub = group / name;
buildRepoTree(sub, child, repos);
}
} else {
RepositoryItem const *repo = repositoryItem(item);
if (repo) {
RepositoryItem newrepo = *repo;
newrepo.name = name;
newrepo.group = group;
item->setData(0, IndexRole, repos->size());
repos->push_back(newrepo);
}
}
}
void MainWindow::refrectRepositories()
{
QList<RepositoryItem> newrepos;
int n = ui->treeWidget_repos->topLevelItemCount();
for (int i = 0; i < n; i++) {
QTreeWidgetItem *item = ui->treeWidget_repos->topLevelItem(i);
buildRepoTree(QString(), item, &newrepos);
}
*getReposPtr() = std::move(newrepos);
saveRepositoryBookmarks();
}
void MainWindow::onRepositoriesTreeDropped()
{
refrectRepositories();
QTreeWidgetItem *item = ui->treeWidget_repos->currentItem();
if (item) item->setExpanded(true);
}
const QPixmap &MainWindow::digitsPixmap() const
{
return m->digits;
}
int MainWindow::digitWidth() const
{
return 5;
}
int MainWindow::digitHeight() const
{
return 7;
}
void MainWindow::drawDigit(QPainter *pr, int x, int y, int n) const
{
int w = digitWidth();
int h = digitHeight();
pr->drawPixmap(x, y, w, h, m->digits, n * w, 0, w, h);
}
QString BasicMainWindow::defaultWorkingDir() const
{
return appsettings()->default_working_dir;
}
QColor MainWindow::color(unsigned int i)
{
unsigned int n = m->graph_color.width();
if (n > 0) {
n--;
if (i > n) i = n;
QRgb const *p = (QRgb const *)m->graph_color.scanLine(0);
return QColor(qRed(p[i]), qGreen(p[i]), qBlue(p[i]));
}
return Qt::black;
}
QString MainWindow::currentWorkingCopyDir() const
{
QString workdir = BasicMainWindow::currentWorkingCopyDir();
if (workdir.isEmpty()) {
RepositoryItem const *repo = selectedRepositoryItem();
if (repo) {
workdir = repo->local_dir;
return workdir;
}
}
return workdir;
}
RepositoryItem const *BasicMainWindow::findRegisteredRepository(QString *workdir) const
{
*workdir = QDir(*workdir).absolutePath();
workdir->replace('\\', '/');
if (Git::isValidWorkingCopy(*workdir)) {
for (RepositoryItem const &item : getRepos()) {
Qt::CaseSensitivity cs = Qt::CaseSensitive;
#ifdef Q_OS_WIN
cs = Qt::CaseInsensitive;
#endif
if (workdir->compare(item.local_dir, cs) == 0) {
return &item;
}
}
}
return nullptr;
}
int MainWindow::repositoryIndex_(QTreeWidgetItem const *item) const
{
if (item) {
int i = item->data(0, IndexRole).toInt();
if (i >= 0 && i < getRepos().size()) {
return i;
}
}
return -1;
}
RepositoryItem const *MainWindow::repositoryItem(QTreeWidgetItem const *item) const
{
int row = repositoryIndex_(item);
auto const &repos = getRepos();
return (row >= 0 && row < repos.size()) ? &repos[row] : nullptr;
}
RepositoryItem const *MainWindow::selectedRepositoryItem() const
{
return repositoryItem(ui->treeWidget_repos->currentItem());
}
static QTreeWidgetItem *newQTreeWidgetItem()
{
auto *item = new QTreeWidgetItem;
item->setSizeHint(0, QSize(20, 20));
return item;
}
QTreeWidgetItem *MainWindow::newQTreeWidgetFolderItem(QString const &name)
{
QTreeWidgetItem *item = newQTreeWidgetItem();
item->setText(0, name);
item->setData(0, IndexRole, GroupItem);
item->setIcon(0, getFolderIcon());
item->setFlags(item->flags() | Qt::ItemIsEditable);
return item;
}
void MainWindow::updateRepositoriesList()
{
QString path = getBookmarksFilePath();
auto *repos = getReposPtr();
*repos = RepositoryBookmark::load(path);
QString filter = getRepositoryFilterText();
ui->treeWidget_repos->clear();
std::map<QString, QTreeWidgetItem *> parentmap;
for (int i = 0; i < repos->size(); i++) {
RepositoryItem const &repo = repos->at(i);
if (!filter.isEmpty() && repo.name.indexOf(filter, 0, Qt::CaseInsensitive) < 0) {
continue;
}
QTreeWidgetItem *parent = nullptr;
{
QString group = repo.group;
if (group.startsWith('/')) {
group = group.mid(1);
}
auto it = parentmap.find(group);
if (it != parentmap.end()) {
parent = it->second;
}
if (!parent) {
QStringList list = group.split('/', QString::SkipEmptyParts);
if (list.isEmpty()) {
list.push_back(tr("Default"));
}
for (QString const &name : list) {
if (name.isEmpty()) continue;
if (!parent) {
auto it = parentmap.find(name);
if (it != parentmap.end()) {
parent = it->second;
} else {
parent = newQTreeWidgetFolderItem(name);
ui->treeWidget_repos->addTopLevelItem(parent);
}
} else {
QTreeWidgetItem *child = newQTreeWidgetFolderItem(name);
parent->addChild(child);
parent = child;
}
parent->setExpanded(true);
}
Q_ASSERT(parent);
parentmap[group] = parent;
}
parent->setData(0, FilePathRole, "");
}
QTreeWidgetItem *child = newQTreeWidgetItem();
child->setText(0, repo.name);
child->setData(0, IndexRole, i);
child->setIcon(0, getRepositoryIcon());
child->setFlags(child->flags() & ~Qt::ItemIsDropEnabled);
parent->addChild(child);
parent->setExpanded(true);
}
}
void MainWindow::showFileList(FilesListType files_list_type)
{
switch (files_list_type) {
case FilesListType::SingleList:
ui->stackedWidget_filelist->setCurrentWidget(ui->page_files);
break;
case FilesListType::SideBySide:
ui->stackedWidget_filelist->setCurrentWidget(ui->page_uncommited);
break;
}
}
void MainWindow::clearFileList()
{
showFileList(FilesListType::SingleList);
ui->listWidget_unstaged->clear();
ui->listWidget_staged->clear();
ui->listWidget_files->clear();
}
void MainWindow::clearDiffView()
{
ui->widget_diff_view->clearDiffView();
}
void MainWindow::clearRepositoryInfo()
{
internalClearRepositoryInfo();
ui->label_repo_name->setText(QString());
ui->label_branch_name->setText(QString());
}
void MainWindow::setRepositoryInfo(QString const &reponame, QString const &brname)
{
ui->label_repo_name->setText(reponame);
ui->label_branch_name->setText(brname);
}
void MainWindow::updateFilesList(QString id, bool wait)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
if (!wait) return;
clearFileList();
Git::FileStatusList stats = g->status();
setUncommitedChanges(!stats.empty());
FilesListType files_list_type = FilesListType::SingleList;
bool staged = false;
auto AddItem = [&](QString const &filename, QString header, int idiff){
if (header.isEmpty()) {
header = "(??\?) "; // damn trigraph
}
QListWidgetItem *item = new QListWidgetItem(header + filename);
item->setData(FilePathRole, filename);
item->setData(DiffIndexRole, idiff);
item->setData(HunkIndexRole, -1);
switch (files_list_type) {
case FilesListType::SingleList:
ui->listWidget_files->addItem(item);
break;
case FilesListType::SideBySide:
if (staged) {
ui->listWidget_staged->addItem(item);
} else {
ui->listWidget_unstaged->addItem(item);
}
break;
}
};
if (id.isEmpty()) {
bool uncommited = isThereUncommitedChanges();
if (uncommited) {
files_list_type = FilesListType::SideBySide;
}
if (!makeDiff(uncommited ? QString() : id, diffResult())) {
return;
}
std::map<QString, int> diffmap;
for (int idiff = 0; idiff < diffResult()->size(); idiff++) {
Git::Diff const &diff = diffResult()->at(idiff);
QString filename = diff.path;
if (!filename.isEmpty()) {
diffmap[filename] = idiff;
}
}
showFileList(files_list_type);
for (Git::FileStatus const &s : stats) {
staged = (s.isStaged() && s.code_y() == ' ');
int idiff = -1;
QString header;
auto it = diffmap.find(s.path1());
if (it != diffmap.end()) {
idiff = it->second;
}
QString path = s.path1();
if (s.code() == Git::FileStatusCode::Unknown) {
qDebug() << "something wrong...";
} else if (s.code() == Git::FileStatusCode::Untracked) {
// nop
} else if (s.isUnmerged()) {
header += "(unmerged) ";
} else if (s.code() == Git::FileStatusCode::AddedToIndex) {
header = "(add) ";
} else if (s.code_x() == 'D' || s.code_y() == 'D' || s.code() == Git::FileStatusCode::DeletedFromIndex) {
header = "(del) ";
} else if (s.code_x() == 'R' || s.code() == Git::FileStatusCode::RenamedInIndex) {
header = "(ren) ";
path = s.path2(); // renamed newer path
} else if (s.code_x() == 'M' || s.code_y() == 'M') {
header = "(chg) ";
}
AddItem(path, header, idiff);
}
} else {
if (!makeDiff(id, diffResult())) {
return;
}
showFileList(files_list_type);
addDiffItems(diffResult(), AddItem);
}
for (Git::Diff const &diff : *diffResult()) {
QString key = GitDiff::makeKey(diff);
(*getDiffCacheMap())[key] = diff;
}
}
void MainWindow::updateFilesList(Git::CommitItem const &commit, bool wait)
{
QString id;
if (Git::isUncommited(commit)) {
// empty id for uncommited changes
} else {
id = commit.commit_id;
}
updateFilesList(id, wait);
}
void MainWindow::updateCurrentFilesList()
{
auto logs = getLogs();
QTableWidgetItem *item = ui->tableWidget_log->item(selectedLogIndex(), 0);
if (!item) return;
int row = item->data(IndexRole).toInt();
int count = (int)logs.size();
if (row < count) {
updateFilesList(logs[row], true);
}
}
void MainWindow::prepareLogTableWidget()
{
QStringList cols = {
tr("Graph"),
tr("Commit"),
tr("Date"),
tr("Author"),
tr("Description"),
};
int n = cols.size();
ui->tableWidget_log->setColumnCount(n);
ui->tableWidget_log->setRowCount(0);
for (int i = 0; i < n; i++) {
QString const &text = cols[i];
auto *item = new QTableWidgetItem(text);
ui->tableWidget_log->setHorizontalHeaderItem(i, item);
}
updateCommitGraph(); // コミットグラフを更新
}
void MainWindow::detectGitServerType(GitPtr g)
{
setServerType(ServerType::Standard);
*ptrGitHub() = GitHubRepositoryInfo();
QString push_url;
QList<Git::Remote> remotes;
g->getRemoteURLs(&remotes);
for (Git::Remote const &r : remotes) {
if (r.purpose == "push") {
push_url = r.url;
}
}
auto Check = [&](QString const &s){
int i = push_url.indexOf(s);
if (i > 0) return i + s.size();
return 0;
};
// check GitHub
int pos = Check("@github.com:");
if (pos == 0) {
pos = Check("://github.com/");
}
if (pos > 0) {
int end = push_url.size();
{
QString s = ".git";
if (push_url.endsWith(s)) {
end -= s.size();
}
}
QString s = push_url.mid(pos, end - pos);
int i = s.indexOf('/');
if (i > 0) {
auto *p = ptrGitHub();
QString user = s.mid(0, i);
QString repo = s.mid(i + 1);
p->owner_account_name = user;
p->repository_name = repo;
}
setServerType(ServerType::GitHub);
}
}
bool BasicMainWindow::fetch(GitPtr g, bool prune)
{
setPtyCondition(PtyCondition::Fetch);
setPtyProcessOk(true);
g->fetch(getPtyProcess(), prune);
while (1) {
if (getPtyProcess()->wait(1)) break;
QApplication::processEvents();
}
return getPtyProcessOk();
}
void MainWindow::clearLog()
{
clearLogs();
clearLabelMap();
setUncommitedChanges(false);
ui->tableWidget_log->clearContents();
ui->tableWidget_log->scrollToTop();
}
void MainWindow::openRepository_(GitPtr g)
{
getObjCache()->setup(g);
if (isValidWorkingCopy(g)) {
bool do_fetch = isRemoteOnline() && (getForceFetch() || appsettings()->automatically_fetch_when_opening_the_repository);
setForceFetch(false);
if (do_fetch) {
if (!fetch(g, false)) {
return;
}
}
clearLog();
clearRepositoryInfo();
detectGitServerType(g);
updateFilesList(QString(), true);
bool canceled = false;
ui->tableWidget_log->setEnabled(false);
// ログを取得
setLogs(retrieveCommitLog(g));
// ブランチを取得
queryBranches(g);
// タグを取得
ptrTagMap()->clear();
QList<Git::Tag> tags = g->tags();
for (Git::Tag const &tag : tags) {
Git::Tag t = tag;
t.id = getObjCache()->getCommitIdFromTag(t.id);
(*ptrTagMap())[t.id].push_back(t);
}
ui->tableWidget_log->setEnabled(true);
updateCommitTableLater();
if (canceled) return;
QString branch_name;
if (currentBranch().flags & Git::Branch::HeadDetachedAt) {
branch_name += QString("(HEAD detached at %1)").arg(currentBranchName());
}
if (currentBranch().flags & Git::Branch::HeadDetachedFrom) {
branch_name += QString("(HEAD detached from %1)").arg(currentBranchName());
}
if (branch_name.isEmpty()) {
branch_name = currentBranchName();
}
QString repo_name = currentRepositoryName();
setRepositoryInfo(repo_name, branch_name);
} else {
clearLog();
clearRepositoryInfo();
}
updateRemoteInfo();
updateWindowTitle(g);
setHeadId(getObjCache()->revParse("HEAD"));
if (isThereUncommitedChanges()) {
Git::CommitItem item;
item.parent_ids.push_back(currentBranch().id);
item.message = tr("Uncommited changes");
auto p = getLogsPtr();
p->insert(p->begin(), item);
}
prepareLogTableWidget();
auto const &logs = getLogs();
const int count = logs.size();
ui->tableWidget_log->setRowCount(count);
int selrow = -1;
for (int row = 0; row < count; row++) {
Git::CommitItem const *commit = &logs[row];
{
auto *item = new QTableWidgetItem;
item->setData(IndexRole, row);
ui->tableWidget_log->setItem(row, 0, item);
}
int col = 1; // カラム0はコミットグラフなので、その次から
auto AddColumn = [&](QString const &text, bool bold, QString const &tooltip){
auto *item = new QTableWidgetItem(text);
if (!tooltip.isEmpty()) {
QString tt = tooltip;
tt.replace('\n', ' ');
tt = tt.toHtmlEscaped();
tt = "<p style='white-space: pre'>" + tt + "</p>";
item->setToolTip(tt);
}
if (bold) {
QFont font = item->font();
font.setBold(true);
item->setFont(font);
}
ui->tableWidget_log->setItem(row, col, item);
col++;
};
QString commit_id;
QString datetime;
QString author;
QString message;
QString message_ex;
bool isHEAD = (commit->commit_id == getHeadId());
bool bold = false;
{
if (Git::isUncommited(*commit)) { // 未コミットの時
bold = true; // 太字
selrow = row;
} else {
if (isHEAD && !isThereUncommitedChanges()) { // HEADで、未コミットがないとき
bold = true; // 太字
selrow = row;
}
commit_id = abbrevCommitID(*commit);
}
datetime = misc::makeDateTimeString(commit->commit_date);
author = commit->author;
message = commit->message;
message_ex = makeCommitInfoText(row, &(*getLabelMap())[row]);
}
AddColumn(commit_id, false, QString());
AddColumn(datetime, false, QString());
AddColumn(author, false, QString());
AddColumn(message, bold, message + message_ex);
ui->tableWidget_log->setRowHeight(row, 24);
}
ui->tableWidget_log->resizeColumnsToContents();
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(false);
ui->tableWidget_log->horizontalHeader()->setStretchLastSection(true);
m->last_focused_file_list = nullptr;
ui->tableWidget_log->setFocus();
setCurrentLogRow(0);
QTableWidgetItem *p = ui->tableWidget_log->item(selrow < 0 ? 0 : selrow, 2);
ui->tableWidget_log->setCurrentItem(p);
m->remote_watcher.setCurrent(currentRemoteName(), currentBranchName());
checkRemoteUpdate();
doUpdateButton();
}
void MainWindow::removeSelectedRepositoryFromBookmark(bool ask)
{
int i = indexOfRepository(ui->treeWidget_repos->currentItem());
removeRepositoryFromBookmark(i, ask);
}
void MainWindow::doUpdateButton()
{
setNetworkingCommandsEnabled(isRemoteOnline());
ui->toolButton_fetch->setDot(getRemoteChanged());
Git::Branch b = currentBranch();
ui->toolButton_push->setNumber(b.ahead > 0 ? b.ahead : -1);
ui->toolButton_pull->setNumber(b.behind > 0 ? b.behind : -1);
}
void MainWindow::updateStatusBarText()
{
QString text;
QWidget *w = qApp->focusWidget();
if (w == ui->treeWidget_repos) {
RepositoryItem const *repo = selectedRepositoryItem();
if (repo) {
text = QString("%1 : %2")
.arg(repo->name)
.arg(misc::normalizePathSeparator(repo->local_dir))
;
}
} else if (w == ui->tableWidget_log) {
QTableWidgetItem *item = ui->tableWidget_log->item(selectedLogIndex(), 0);
if (item) {
auto const &logs = getLogs();
int row = item->data(IndexRole).toInt();
if (row < (int)logs.size()) {
Git::CommitItem const &commit = logs[row];
if (Git::isUncommited(commit)) {
text = tr("Uncommited changes");
} else {
QString id = commit.commit_id;
text = QString("%1 : %2%3")
.arg(id.mid(0, 7))
.arg(commit.message)
.arg(makeCommitInfoText(row, nullptr))
;
}
}
}
}
setStatusBarText(text);
}
void MainWindow::on_action_commit_triggered()
{
commit();
}
void MainWindow::on_action_fetch_triggered()
{
if (!isRemoteOnline()) return;
reopenRepository(true, [&](GitPtr g){
fetch(g, false);
});
}
void MainWindow::on_action_fetch_prune_triggered()
{
if (!isRemoteOnline()) return;
reopenRepository(true, [&](GitPtr g){
fetch(g, true);
});
}
void MainWindow::on_action_push_triggered()
{
push();
}
void MainWindow::on_action_pull_triggered()
{
if (!isRemoteOnline()) return;
reopenRepository(true, [&](GitPtr g){
setPtyCondition(PtyCondition::Pull);
setPtyProcessOk(true);
g->pull(getPtyProcess());
while (1) {
if (getPtyProcess()->wait(1)) break;
QApplication::processEvents();
}
});
}
void MainWindow::on_toolButton_push_clicked()
{
ui->action_push->trigger();
}
void MainWindow::on_toolButton_pull_clicked()
{
ui->action_pull->trigger();
}
void MainWindow::on_treeWidget_repos_currentItemChanged(QTreeWidgetItem * /*current*/, QTreeWidgetItem * /*previous*/)
{
updateStatusBarText();
}
void MainWindow::on_treeWidget_repos_itemDoubleClicked(QTreeWidgetItem * /*item*/, int /*column*/)
{
openSelectedRepository();
}
void BasicMainWindow::execCommitPropertyDialog(QWidget *parent, Git::CommitItem const *commit)
{
CommitPropertyDialog dlg(parent, this, commit);
dlg.exec();
}
int MainWindow::indexOfRepository(QTreeWidgetItem const *treeitem) const
{
if (!treeitem) return -1;
return treeitem->data(0, IndexRole).toInt();
}
void MainWindow::on_treeWidget_repos_customContextMenuRequested(const QPoint &pos)
{
QTreeWidgetItem *treeitem = ui->treeWidget_repos->currentItem();
if (!treeitem) return;
RepositoryItem const *repo = repositoryItem(treeitem);
int index = indexOfRepository(treeitem);
if (isGroupItem(treeitem)) { // group item
QMenu menu;
QAction *a_add_new_group = menu.addAction(tr("&Add new group"));
QAction *a_delete_group = menu.addAction(tr("&Delete group"));
QAction *a_rename_group = menu.addAction(tr("&Rename group"));
QPoint pt = ui->treeWidget_repos->mapToGlobal(pos);
QAction *a = menu.exec(pt + QPoint(8, -8));
if (a) {
if (a == a_add_new_group) {
QTreeWidgetItem *child = newQTreeWidgetFolderItem(tr("New group"));
treeitem->addChild(child);
child->setFlags(child->flags() | Qt::ItemIsEditable);
ui->treeWidget_repos->setCurrentItem(child);
return;
}
if (a == a_delete_group) {
QTreeWidgetItem *parent = treeitem->parent();
if (parent) {
int i = parent->indexOfChild(treeitem);
delete parent->takeChild(i);
} else {
int i = ui->treeWidget_repos->indexOfTopLevelItem(treeitem);
delete ui->treeWidget_repos->takeTopLevelItem(i);
}
refrectRepositories();
return;
}
if (a == a_rename_group) {
ui->treeWidget_repos->editItem(treeitem);
return;
}
}
} else if (repo) { // repository item
QString open_terminal = tr("Open &terminal");
QString open_commandprompt = tr("Open command promp&t");
QMenu menu;
QAction *a_open = menu.addAction(tr("&Open"));
menu.addSeparator();
#ifdef Q_OS_WIN
QAction *a_open_terminal = menu.addAction(open_commandprompt);
(void)open_terminal;
#else
QAction *a_open_terminal = menu.addAction(open_terminal);
(void)open_commandprompt;
#endif
+ a_open_terminal->setIcon(QIcon(":/image/terminal.svg"));
+
QAction *a_open_folder = menu.addAction(tr("Open &folder"));
+ a_open_folder->setIcon(QIcon(":/image/explorer.svg"));
+
menu.addSeparator();
+
QAction *a_remove = menu.addAction(tr("&Remove"));
menu.addSeparator();
+
QAction *a_properties = addMenuActionProperty(&menu);
QPoint pt = ui->treeWidget_repos->mapToGlobal(pos);
QAction *a = menu.exec(pt + QPoint(8, -8));
+
if (a) {
if (a == a_open) {
openSelectedRepository();
return;
}
if (a == a_open_folder) {
openExplorer(repo);
return;
}
if (a == a_open_terminal) {
openTerminal(repo);
return;
}
if (a == a_remove) {
removeRepositoryFromBookmark(index, true);
return;
}
if (a == a_properties) {
execRepositoryPropertyDialog(repo->local_dir);
return;
}
}
}
}
void MainWindow::on_tableWidget_log_customContextMenuRequested(const QPoint &pos)
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
bool is_valid_commit_id = Git::isValidID(commit->commit_id);
int row = selectedLogIndex();
QMenu menu;
QAction *a_copy_id_7_letters = menu.addAction(tr("Copy commit id (7 letters)"));
QAction *a_copy_id_complete = menu.addAction(tr("Copy commit id (completely)"));
menu.addSeparator();
QAction *a_checkout = menu.addAction(tr("Checkout/Branch..."));
menu.addSeparator();
QAction *a_edit_comment = nullptr;
if (row == 0 && currentBranch().ahead > 0) {
a_edit_comment = menu.addAction(tr("Edit comment..."));
}
QAction *a_merge = is_valid_commit_id ? menu.addAction(tr("Merge")) : nullptr;
QAction *a_rebase = is_valid_commit_id ? menu.addAction(tr("Rebase")) : nullptr;
QAction *a_cherrypick = is_valid_commit_id ? menu.addAction(tr("Cherry-pick")) : nullptr;
QAction *a_edit_tags = is_valid_commit_id ? menu.addAction(tr("Edit tags...")) : nullptr;
QAction *a_revert = is_valid_commit_id ? menu.addAction(tr("Revert")) : nullptr;
QAction *a_reset_head = nullptr;
#if 0 // 下手に使うと危険なので、とりあえず無効にしておく
if (is_valid_commit_id && commit->commit_id == head_id_) {
a_reset_head = menu.addAction(tr("Reset HEAD"));
}
#endif
menu.addSeparator();
QAction *a_delbranch = is_valid_commit_id ? menu.addAction(tr("Delete branch...")) : nullptr;
QAction *a_delrembranch = remoteBranches(commit->commit_id, nullptr).isEmpty() ? nullptr : menu.addAction(tr("Delete remote branch..."));
menu.addSeparator();
QAction *a_explore = is_valid_commit_id ? menu.addAction(tr("Explore")) : nullptr;
QAction *a_properties = addMenuActionProperty(&menu);
QAction *a = menu.exec(ui->tableWidget_log->viewport()->mapToGlobal(pos) + QPoint(8, -8));
if (a) {
if (a == a_copy_id_7_letters) {
qApp->clipboard()->setText(commit->commit_id.mid(0, 7));
return;
}
if (a == a_copy_id_complete) {
qApp->clipboard()->setText(commit->commit_id);
return;
}
if (a == a_properties) {
execCommitPropertyDialog(this, commit);
return;
}
if (a == a_edit_comment) {
commitAmend();
return;
}
if (a == a_checkout) {
checkout(this, commit);
return;
}
if (a == a_delbranch) {
deleteBranch(commit);
return;
}
if (a == a_delrembranch) {
deleteRemoteBranch(commit);
return;
}
if (a == a_merge) {
mergeBranch(commit);
return;
}
if (a == a_rebase) {
rebaseBranch(commit);
return;
}
if (a == a_cherrypick) {
cherrypick(commit);
return;
}
if (a == a_edit_tags) {
ui->action_edit_tags->trigger();
return;
}
if (a == a_revert) {
revertCommit();
return;
}
if (a == a_explore) {
execCommitExploreWindow(this, commit);
return;
}
if (a == a_reset_head) {
reopenRepository(false, [](GitPtr g){
g->reset_head1();
});
return;
}
}
}
}
void MainWindow::on_listWidget_files_customContextMenuRequested(const QPoint &pos)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QMenu menu;
QAction *a_delete = menu.addAction(tr("Delete"));
QAction *a_untrack = menu.addAction(tr("Untrack"));
QAction *a_history = menu.addAction(tr("History"));
QAction *a_blame = menu.addAction(tr("Blame"));
QAction *a_properties = addMenuActionProperty(&menu);
QPoint pt = ui->listWidget_files->mapToGlobal(pos) + QPoint(8, -8);
QAction *a = menu.exec(pt);
if (a) {
QListWidgetItem *item = ui->listWidget_files->currentItem();
if (a == a_delete) {
if (askAreYouSureYouWantToRun("Delete", tr("Delete selected files."))) {
for_each_selected_files([&](QString const &path){
g->removeFile(path);
g->chdirexec([&](){
QFile(path).remove();
return true;
});
});
openRepository(false);
}
} else if (a == a_untrack) {
if (askAreYouSureYouWantToRun("Untrack", tr("rm --cached files"))) {
for_each_selected_files([&](QString const &path){
g->rm_cached(path);
});
openRepository(false);
}
} else if (a == a_history) {
execFileHistory(item);
} else if (a == a_blame) {
blame(item);
} else if (a == a_properties) {
execFilePropertyDialog(item);
}
}
}
void MainWindow::on_listWidget_unstaged_customContextMenuRequested(const QPoint &pos)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QList<QListWidgetItem *> items = ui->listWidget_unstaged->selectedItems();
if (!items.isEmpty()) {
QMenu menu;
QAction *a_stage = menu.addAction(tr("Stage"));
QAction *a_reset_file = menu.addAction(tr("Reset"));
QAction *a_ignore = menu.addAction(tr("Ignore"));
QAction *a_delete = menu.addAction(tr("Delete"));
QAction *a_untrack = menu.addAction(tr("Untrack"));
QAction *a_history = menu.addAction(tr("History"));
QAction *a_blame = menu.addAction(tr("Blame"));
QAction *a_properties = addMenuActionProperty(&menu);
QPoint pt = ui->listWidget_unstaged->mapToGlobal(pos) + QPoint(8, -8);
QAction *a = menu.exec(pt);
if (a) {
QListWidgetItem *item = ui->listWidget_unstaged->currentItem();
if (a == a_stage) {
for_each_selected_files([&](QString const &path){
g->stage(path);
});
updateCurrentFilesList();
return;
}
if (a == a_reset_file) {
QStringList paths;
for_each_selected_files([&](QString const &path){
paths.push_back(path);
});
resetFile(paths);
return;
}
if (a == a_ignore) {
QString gitignore_path = currentWorkingCopyDir() / ".gitignore";
if (items.size() == 1) {
QString file = getFilePath(items[0]);
EditGitIgnoreDialog dlg(this, gitignore_path, file);
if (dlg.exec() == QDialog::Accepted) {
QString appending = dlg.text();
if (!appending.isEmpty()) {
QString text;
QString path = gitignore_path;
path.replace('/', QDir::separator());
{
QFile file(path);
if (file.open(QFile::ReadOnly)) {
text += QString::fromUtf8(file.readAll());
}
}
size_t n = text.size();
if (n > 0 && text[(int)n - 1] != '\n') {
text += '\n'; // 最後に改行を追加
}
text += appending + '\n';
{
QFile file(path);
if (file.open(QFile::WriteOnly)) {
file.write(text.toUtf8());
}
}
updateCurrentFilesList();
return;
}
} else {
return;
}
}
QString append;
for_each_selected_files([&](QString const &path){
if (path == ".gitignore") {
// skip
} else {
append += path + '\n';
}
});
if (TextEditDialog::editFile(this, gitignore_path, ".gitignore", append)) {
updateCurrentFilesList();
}
return;
}
if (a == a_delete) {
if (askAreYouSureYouWantToRun("Delete", "Delete selected files.")) {
for_each_selected_files([&](QString const &path){
g->removeFile(path);
g->chdirexec([&](){
QFile(path).remove();
return true;
});
});
openRepository(false);
}
return;
}
if (a == a_untrack) {
if (askAreYouSureYouWantToRun("Untrack", "rm --cached")) {
for_each_selected_files([&](QString const &path){
g->rm_cached(path);
});
openRepository(false);
}
return;
}
if (a == a_history) {
execFileHistory(item);
return;
}
if (a == a_blame) {
blame(item);
return;
}
if (a == a_properties) {
execFilePropertyDialog(item);
return;
}
}
}
}
void MainWindow::on_listWidget_staged_customContextMenuRequested(const QPoint &pos)
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QListWidgetItem *item = ui->listWidget_staged->currentItem();
if (item) {
QString path = getFilePath(item);
QString fullpath = currentWorkingCopyDir() / path;
if (QFileInfo(fullpath).isFile()) {
QMenu menu;
QAction *a_unstage = menu.addAction(tr("Unstage"));
QAction *a_history = menu.addAction(tr("History"));
QAction *a_blame = menu.addAction(tr("Blame"));
QAction *a_properties = addMenuActionProperty(&menu);
QPoint pt = ui->listWidget_staged->mapToGlobal(pos) + QPoint(8, -8);
QAction *a = menu.exec(pt);
if (a) {
QListWidgetItem *item = ui->listWidget_unstaged->currentItem();
if (a == a_unstage) {
g->unstage(path);
openRepository(false);
} else if (a == a_history) {
execFileHistory(item);
} else if (a == a_blame) {
blame(item);
} else if (a == a_properties) {
execFilePropertyDialog(item);
}
}
}
}
}
QStringList MainWindow::selectedFiles_(QListWidget *listwidget) const
{
QStringList list;
QList<QListWidgetItem *> items = listwidget->selectedItems();
for (QListWidgetItem *item : items) {
QString path = getFilePath(item);
list.push_back(path);
}
return list;
}
QStringList MainWindow::selectedFiles() const
{
if (m->last_focused_file_list == ui->listWidget_files) return selectedFiles_(ui->listWidget_files);
if (m->last_focused_file_list == ui->listWidget_staged) return selectedFiles_(ui->listWidget_staged);
if (m->last_focused_file_list == ui->listWidget_unstaged) return selectedFiles_(ui->listWidget_unstaged);
return QStringList();
}
void MainWindow::for_each_selected_files(std::function<void(QString const&)> fn)
{
for (QString path : selectedFiles()) {
fn(path);
}
}
void BasicMainWindow::execFileHistory(QListWidgetItem *item)
{
if (item) {
QString path = getFilePath(item);
if (!path.isEmpty()) {
execFileHistory(path);
}
}
}
void MainWindow::on_action_open_existing_working_copy_triggered()
{
QString dir = defaultWorkingDir();
dir = QFileDialog::getExistingDirectory(this, tr("Add existing working copy"), dir);
addWorkingCopyDir(dir, false);
}
void MainWindow::on_action_view_refresh_triggered()
{
openRepository(true);
}
void MainWindow::on_tableWidget_log_currentItemChanged(QTableWidgetItem * /*current*/, QTableWidgetItem * /*previous*/)
{
clearFileList();
QTableWidgetItem *item = ui->tableWidget_log->item(selectedLogIndex(), 0);
if (!item) return;
auto const &logs = getLogs();
int row = item->data(IndexRole).toInt();
if (row < (int)logs.size()) {
updateStatusBarText();
*ptrUpdateFilesListCounter() = 200;
}
}
void MainWindow::on_toolButton_stage_clicked()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->stage(selectedFiles());
updateCurrentFilesList();
}
void MainWindow::on_toolButton_unstage_clicked()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->unstage(selectedFiles());
updateCurrentFilesList();
}
void MainWindow::on_toolButton_select_all_clicked()
{
if (ui->listWidget_unstaged->count() > 0) {
ui->listWidget_unstaged->setFocus();
ui->listWidget_unstaged->selectAll();
} else if (ui->listWidget_staged->count() > 0) {
ui->listWidget_staged->setFocus();
ui->listWidget_staged->selectAll();
}
}
void MainWindow::on_toolButton_commit_clicked()
{
ui->action_commit->trigger();
}
void MainWindow::on_action_edit_global_gitconfig_triggered()
{
QString dir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
QString path = dir / ".gitconfig";
editFile(path, ".gitconfig");
}
void MainWindow::on_action_edit_git_config_triggered()
{
QString dir = currentWorkingCopyDir();
QString path = dir / ".git/config";
editFile(path, ".git/config");
}
void MainWindow::on_action_edit_gitignore_triggered()
{
QString dir = currentWorkingCopyDir();
QString path = dir / ".gitignore";
if (editFile(path, ".gitignore")) {
updateCurrentFilesList();
}
}
int MainWindow::selectedLogIndex() const
{
auto const &logs = getLogs();
int i = ui->tableWidget_log->currentRow();
if (i >= 0 && i < (int)logs.size()) {
return i;
}
return -1;
}
void MainWindow::updateDiffView(QListWidgetItem *item)
{
clearDiffView();
m->last_selected_file_item = item;
if (!item) return;
int idiff = indexOfDiff(item);
if (idiff >= 0 && idiff < diffResult()->size()) {
Git::Diff const &diff = diffResult()->at(idiff);
QString key = GitDiff::makeKey(diff);
auto it = getDiffCacheMap()->find(key);
if (it != getDiffCacheMap()->end()) {
auto const &logs = getLogs();
int row = ui->tableWidget_log->currentRow();
bool uncommited = (row >= 0 && row < (int)logs.size() && Git::isUncommited(logs[row]));
ui->widget_diff_view->updateDiffView(it->second, uncommited);
}
}
}
void MainWindow::updateDiffView()
{
updateDiffView(m->last_selected_file_item);
}
void MainWindow::updateUnstagedFileCurrentItem()
{
updateDiffView(ui->listWidget_unstaged->currentItem());
}
void MainWindow::updateStagedFileCurrentItem()
{
updateDiffView(ui->listWidget_staged->currentItem());
}
void MainWindow::on_listWidget_unstaged_currentRowChanged(int /*currentRow*/)
{
updateUnstagedFileCurrentItem();
}
void MainWindow::on_listWidget_staged_currentRowChanged(int /*currentRow*/)
{
updateStagedFileCurrentItem();
}
void MainWindow::on_listWidget_files_currentRowChanged(int /*currentRow*/)
{
updateDiffView(ui->listWidget_files->currentItem());
}
void MainWindow::setWatchRemoteInterval(int mins)
{
if (mins > 0) {
m->remote_watcher_timer.start(mins * 60000);
} else {
m->remote_watcher_timer.stop();
}
}
void MainWindow::setRemoteMonitoringEnabled(bool enable)
{
if (enable) {
setWatchRemoteInterval(appsettings()->watch_remote_changes_every_mins);
} else {
setWatchRemoteInterval(0);
}
}
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
if (QApplication::modalWindow()) return;
if (event->mimeData()->hasUrls()) {
event->acceptProposedAction();
event->accept();
}
}
void MainWindow::timerEvent(QTimerEvent *)
{
bool running = getPtyProcess()->isRunning();
if (ui->toolButton_stop_process->isEnabled() != running) {
ui->toolButton_stop_process->setEnabled(running);
ui->action_stop_process->setEnabled(running);
setNetworkingCommandsEnabled(!running);
}
if (!running) {
setInteractionMode(InteractionMode::None);
}
while (1) {
char tmp[1024];
int len = getPtyProcess()->readOutput(tmp, sizeof(tmp));
if (len < 1) break;
writeLog(tmp, len);
}
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if (QApplication::focusWidget() == ui->widget_log) {
int c = event->key();
auto write_char = [&](char c){
if (getPtyProcess()->isRunning()) {
getPtyProcess()->writeInput(&c, 1);
}
};
auto write_text = [&](QString const &str){
std::string s = str.toStdString();
for (char i : s) {
write_char(i);
}
};
if (c == Qt::Key_Return || c == Qt::Key_Enter) {
write_char('\n');
} else {
QString text = event->text();
write_text(text);
}
}
}
void MainWindow::on_action_edit_settings_triggered()
{
SettingsDialog dlg(this);
if (dlg.exec() == QDialog::Accepted) {
ApplicationSettings const &newsettings = dlg.settings();
setAppSettings(newsettings);
setGitCommand(appsettings()->git_command, false);
setFileCommand(appsettings()->file_command, false);
setGpgCommand(appsettings()->gpg_command, false);
setRemoteMonitoringEnabled(true);
}
}
void MainWindow::onCloneCompleted(bool success, QVariant const &userdata)
{
if (success) {
RepositoryItem r = userdata.value<RepositoryItem>();
saveRepositoryBookmark(r);
setCurrentRepository(r, false);
openRepository(true);
}
}
void MainWindow::onPtyProcessCompleted(bool /*ok*/, QVariant const &userdata)
{
switch (getPtyCondition()) {
case PtyCondition::Clone:
onCloneCompleted(getPtyProcessOk(), userdata);
break;
}
setPtyCondition(PtyCondition::None);
}
void MainWindow::on_action_clone_triggered()
{
clone();
}
void MainWindow::on_action_about_triggered()
{
AboutDialog dlg(this);
dlg.exec();
}
void MainWindow::on_toolButton_clone_clicked()
{
ui->action_clone->trigger();
}
void MainWindow::on_toolButton_fetch_clicked()
{
ui->action_fetch->trigger();
}
void MainWindow::clearRepoFilter()
{
ui->lineEdit_filter->clear();
}
void MainWindow::appendCharToRepoFilter(ushort c)
{
if (QChar(c).isLetter()) {
c = QChar(c).toLower().unicode();
}
ui->lineEdit_filter->setText(getRepositoryFilterText() + c);
}
void MainWindow::backspaceRepoFilter()
{
QString s = getRepositoryFilterText();
int n = s.size();
if (n > 0) {
s = s.mid(0, n - 1);
}
ui->lineEdit_filter->setText(s);
}
void MainWindow::on_lineEdit_filter_textChanged(QString const &text)
{
setRepositoryFilterText(text);
updateRepositoriesList();
}
void MainWindow::on_toolButton_erase_filter_clicked()
{
clearRepoFilter();
ui->lineEdit_filter->setFocus();
}
void MainWindow::deleteTags(QStringList const &tagnames)
{
int row = ui->tableWidget_log->currentRow();
internalDeleteTags(tagnames);
ui->tableWidget_log->selectRow(row);
}
void MainWindow::revertCommit()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
g->revert(commit->commit_id);
openRepository(false);
}
}
bool MainWindow::addTag(QString const &name)
{
int row = ui->tableWidget_log->currentRow();
bool ok = internalAddTag(name);
ui->tableWidget_log->selectRow(row);
return ok;
}
void MainWindow::on_action_push_all_tags_triggered()
{
reopenRepository(false, [&](GitPtr g){
g->push(true);
});
}
void MainWindow::on_tableWidget_log_itemDoubleClicked(QTableWidgetItem *)
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit) {
execCommitPropertyDialog(this, commit);
}
}
void MainWindow::on_listWidget_unstaged_itemDoubleClicked(QListWidgetItem * item)
{
execFilePropertyDialog(item);
}
void MainWindow::on_listWidget_staged_itemDoubleClicked(QListWidgetItem *item)
{
execFilePropertyDialog(item);
}
void MainWindow::on_listWidget_files_itemDoubleClicked(QListWidgetItem *item)
{
execFilePropertyDialog(item);
}
QListWidgetItem *MainWindow::currentFileItem() const
{
QListWidget *listwidget = nullptr;
if (ui->stackedWidget_filelist->currentWidget() == ui->page_uncommited) {
QWidget *w = qApp->focusWidget();
if (w == ui->listWidget_unstaged) {
listwidget = ui->listWidget_unstaged;
} else if (w == ui->listWidget_staged) {
listwidget = ui->listWidget_staged;
}
} else {
listwidget = ui->listWidget_files;
}
if (listwidget) {
return listwidget->currentItem();
}
return nullptr;
}
void MainWindow::on_action_set_config_user_triggered()
{
Git::User global_user;
Git::User repo_user;
GitPtr g = git();
if (isValidWorkingCopy(g)) {
repo_user = g->getUser(Git::Source::Local);
}
global_user = g->getUser(Git::Source::Global);
execSetUserDialog(global_user, repo_user, currentRepositoryName());
}
void MainWindow::on_action_window_log_triggered(bool checked)
{
ui->dockWidget_log->setVisible(checked);
}
void MainWindow::on_action_repo_jump_triggered()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
NamedCommitList items = namedCommitItems(Branches | Tags | Remotes);
{
NamedCommitItem head;
head.name = "HEAD";
head.id = getHeadId();
items.push_front(head);
}
JumpDialog dlg(this, items);
if (dlg.exec() == QDialog::Accepted) {
QString text = dlg.text();
if (text.isEmpty()) return;
QString id = g->rev_parse(text);
if (id.isEmpty() && Git::isValidID(text)) {
QStringList list = findGitObject(text);
if (list.isEmpty()) {
QMessageBox::warning(this, tr("Jump"), QString("%1\n\n").arg(text) + tr("No such commit"));
return;
}
ObjectBrowserDialog dlg2(this, list);
if (dlg2.exec() == QDialog::Accepted) {
id = dlg2.text();
if (id.isEmpty()) return;
}
}
if (g->objectType(id) == "tag") {
id = getObjCache()->getCommitIdFromTag(id);
}
int row = rowFromCommitId(id);
if (row < 0) {
QMessageBox::warning(this, tr("Jump"), QString("%1\n(%2)\n\n").arg(text).arg(id) + tr("No such commit"));
} else {
setCurrentLogRow(row);
}
}
}
void MainWindow::mergeBranch(Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->mergeBranch(commit->commit_id);
openRepository(true);
}
void MainWindow::rebaseBranch(Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
QString text = tr("Are you sure you want to rebase the commit ?");
text += "\n\n";
text += "> git rebase " + commit->commit_id;
int r = QMessageBox::information(this, tr("Rebase"), text, QMessageBox::Ok, QMessageBox::Cancel);
if (r == QMessageBox::Ok) {
g->rebaseBranch(commit->commit_id);
openRepository(true);
}
}
void MainWindow::cherrypick(Git::CommitItem const *commit)
{
if (!commit) return;
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->cherrypick(commit->commit_id);
openRepository(true);
}
void MainWindow::on_action_repo_checkout_triggered()
{
checkout();
}
void MainWindow::on_action_delete_branch_triggered()
{
deleteBranch();
}
void MainWindow::on_toolButton_terminal_clicked()
{
openTerminal(nullptr);
}
void MainWindow::on_toolButton_explorer_clicked()
{
openExplorer(nullptr);
}
void MainWindow::on_action_reset_HEAD_1_triggered()
{
GitPtr g = git();
if (!isValidWorkingCopy(g)) return;
g->reset_head1();
openRepository(false);
}
void MainWindow::on_action_create_a_repository_triggered()
{
createRepository(QString());
}
void MainWindow::setNetworkingCommandsEnabled(bool f)
{
ui->action_clone->setEnabled(f);
ui->toolButton_clone->setEnabled(f);
if (!Git::isValidWorkingCopy(currentWorkingCopyDir())) {
f = false;
}
ui->action_fetch->setEnabled(f);
ui->action_fetch_prune->setEnabled(f);
ui->action_pull->setEnabled(f);
ui->action_push->setEnabled(f);
ui->action_push_u->setEnabled(f);
ui->action_push_all_tags->setEnabled(f);
ui->toolButton_fetch->setEnabled(f);
ui->toolButton_pull->setEnabled(f);
ui->toolButton_push->setEnabled(f);
}
bool MainWindow::isRemoteOnline() const
{
return m->is_online_mode;
}
void MainWindow::setRemoteOnline(bool f)
{
m->is_online_mode = f;
QRadioButton *rb = nullptr;
rb = f ? ui->radioButton_remote_online : ui->radioButton_remote_offline;
rb->blockSignals(true);
rb->click();
rb->blockSignals(false);
ui->action_online->setCheckable(true);
ui->action_offline->setCheckable(true);
ui->action_online->setChecked(f);
ui->action_offline->setChecked(!f);
setNetworkingCommandsEnabled(f);
MySettings s;
s.beginGroup("Remote");
s.setValue("Online", f);
s.endGroup();
}
void MainWindow::on_radioButton_remote_online_clicked()
{
setRemoteOnline(true);
}
void MainWindow::on_radioButton_remote_offline_clicked()
{
setRemoteOnline(false);
}
void MainWindow::on_verticalScrollBar_log_valueChanged(int)
{
ui->widget_log->refrectScrollBar();
}
void MainWindow::on_horizontalScrollBar_log_valueChanged(int)
{
ui->widget_log->refrectScrollBar();
}
void MainWindow::on_toolButton_stop_process_clicked()
{
abortPtyProcess();
}
void MainWindow::on_action_stop_process_triggered()
{
abortPtyProcess();
}
void MainWindow::on_action_exit_triggered()
{
close();
}
void MainWindow::on_action_reflog_triggered()
{
GitPtr g = git();
Git::ReflogItemList reflog;
g->reflog(&reflog);
ReflogWindow dlg(this, this, reflog);
dlg.exec();
}
void MainWindow::blame(QListWidgetItem *item)
{
QList<BlameItem> list;
QString path = getFilePath(item);
{
GitPtr g = git();
QByteArray ba = g->blame(path);
if (!ba.isEmpty()) {
char const *begin = ba.data();
char const *end = begin + ba.size();
list = BlameWindow::parseBlame(begin, end);
}
}
if (!list.isEmpty()) {
qApp->setOverrideCursor(Qt::WaitCursor);
BlameWindow win(this, path, list);
qApp->restoreOverrideCursor();
win.exec();
}
}
void MainWindow::blame()
{
blame(currentFileItem());
}
void MainWindow::on_action_repository_property_triggered()
{
execRepositoryPropertyDialog(currentWorkingCopyDir());
}
void MainWindow::on_action_set_gpg_signing_triggered()
{
GitPtr g = git();
QString global_key_id = g->signingKey(Git::Source::Global);
QString repository_key_id;
if (g->isValidWorkingCopy()) {
repository_key_id = g->signingKey(Git::Source::Local);
}
SetGpgSigningDialog dlg(this, currentRepositoryName(), global_key_id, repository_key_id);
if (dlg.exec() == QDialog::Accepted) {
g->setSigningKey(dlg.id(), dlg.isGlobalChecked());
}
}
void MainWindow::execAreYouSureYouWantToContinueConnectingDialog()
{
using TheDlg = AreYouSureYouWantToContinueConnectingDialog;
setInteractionMode(InteractionMode::Busy);
QApplication::restoreOverrideCursor();
TheDlg dlg(this);
if (dlg.exec() == QDialog::Accepted) {
TheDlg::Result r = dlg.result();
if (r == TheDlg::Result::Yes) {
getPtyProcess()->writeInput("yes\n", 4);
} else {
setPtyProcessOk(false); // abort
getPtyProcess()->writeInput("no\n", 3);
QThread::msleep(300);
stopPtyProcess();
}
} else {
ui->widget_log->setFocus();
setInteractionCanceled(true);
}
setInteractionMode(InteractionMode::Busy);
}
void MainWindow::onLogIdle()
{
if (interactionCanceled()) return;
if (interactionMode() != InteractionMode::None) return;
static char const are_you_sure_you_want_to_continue_connecting[] = "Are you sure you want to continue connecting (yes/no)?";
static char const enter_passphrase[] = "Enter passphrase: ";
static char const enter_passphrase_for_key[] = "Enter passphrase for key '";
static char const fatal_authentication_failed_for[] = "fatal: Authentication failed for '";
std::vector<char> vec;
ui->widget_log->retrieveLastText(&vec, 100);
if (!vec.empty()) {
std::string line;
int n = (int)vec.size();
int i = n;
while (i > 0) {
i--;
if (i + 1 < n && vec[i] == '\n') {
i++;
line.assign(&vec[i], n - i);
break;
}
}
if (!line.empty()) {
auto ExecLineEditDialog = [&](QWidget *parent, QString const &title, QString const &prompt, QString const &val, bool password){
LineEditDialog dlg(parent, title, prompt, val, password);
if (dlg.exec() == QDialog::Accepted) {
std::string ret = dlg.text().toStdString();
std::string str = ret + '\n';
getPtyProcess()->writeInput(str.c_str(), str.size());
return ret;
}
abortPtyProcess();
return std::string();
};
auto Match = [&](char const *str){
int n = strlen(str);
if (strncmp(line.c_str(), str, n) == 0) {
char const *p = line.c_str() + n;
while (1) {
if (!*p) return true;
if (!isspace((unsigned char)*p)) break;
p++;
}
}
return false;
};
auto StartsWith = [&](char const *str){
char const *p = line.c_str();
while (*str) {
if (*p != *str) return false;
str++;
p++;
}
return true;
};
if (Match(are_you_sure_you_want_to_continue_connecting)) {
execAreYouSureYouWantToContinueConnectingDialog();
return;
}
if (line == enter_passphrase) {
ExecLineEditDialog(this, "Passphrase", QString::fromStdString(line), QString(), true);
return;
}
if (StartsWith(enter_passphrase_for_key)) {
std::string keyfile;
{
int i = strlen(enter_passphrase_for_key);
char const *p = line.c_str() + i;
char const *q = strrchr(p, ':');
if (q && p + 2 < q && q[-1] == '\'') {
keyfile.assign(p, q - 1);
}
}
if (!keyfile.empty()) {
if (keyfile == sshPassphraseUser() && !sshPassphrasePass().empty()) {
std::string text = sshPassphrasePass() + '\n';
getPtyProcess()->writeInput(text.c_str(), text.size());
} else {
std::string secret = ExecLineEditDialog(this, "Passphrase for key", QString::fromStdString(line), QString(), true);
sshSetPassphrase(keyfile, secret);
}
return;
}
}
char const *begin = line.c_str();
char const *end = line.c_str() + line.size();
auto Input = [&](QString const &title, bool password, std::string *value){
Q_ASSERT(value);
std::string header = QString("%1 for '").arg(title).toStdString();
if (strncmp(begin, header.c_str(), header.size()) == 0) {
QString msg;
if (memcmp(end - 2, "':", 2) == 0) {
msg = QString::fromUtf8(begin, end - begin - 1);
} else if (memcmp(end - 3, "': ", 3) == 0) {
msg = QString::fromUtf8(begin, end - begin - 2);
}
if (!msg.isEmpty()) {
std::string s = ExecLineEditDialog(this, title, msg, value ? QString::fromStdString(*value) : QString(), password);
*value = s;
return true;
}
}
return false;
};
std::string uid = httpAuthenticationUser();
std::string pwd = httpAuthenticationPass();
bool ok = false;
if (Input("Username", false, &uid)) ok = true;
if (Input("Password", true, &pwd)) ok = true;
if (ok) {
httpSetAuthentication(uid, pwd);
return;
}
if (StartsWith(fatal_authentication_failed_for)) {
QMessageBox::critical(this, tr("Authentication Failed"), QString::fromStdString(line));
abortPtyProcess();
return;
}
}
}
}
void MainWindow::on_action_edit_tags_triggered()
{
Git::CommitItem const *commit = selectedCommitItem();
if (commit && Git::isValidID(commit->commit_id)) {
EditTagsDialog dlg(this, commit);
dlg.exec();
}
}
void MainWindow::on_action_push_u_triggered()
{
pushSetUpstream(false);
}
void MainWindow::on_action_delete_remote_branch_triggered()
{
deleteRemoteBranch(selectedCommitItem());
}
void MainWindow::on_action_terminal_triggered()
{
auto const *repo = &currentRepository();
openTerminal(repo);
}
void MainWindow::on_action_explorer_triggered()
{
auto const *repo = &currentRepository();
openExplorer(repo);
}
void MainWindow::on_action_reset_hard_triggered()
{
doGitCommand([&](GitPtr g){
g->reset_hard();
});
}
void MainWindow::on_action_clean_df_triggered()
{
doGitCommand([&](GitPtr g){
g->clean_df();
});
}
void MainWindow::postOpenRepositoryFromGitHub(QString const &username, QString const &reponame)
{
QVariantList list;
list.push_back(username);
list.push_back(reponame);
postUserFunctionEvent([&](QVariant const &v){
QVariantList l = v.toList();
QString uname = l[0].toString();
QString rname = l[1].toString();
CloneFromGitHubDialog dlg(this, uname, rname);
if (dlg.exec() == QDialog::Accepted) {
clone(dlg.url());
}
}, QVariant(list));
}
void MainWindow::on_action_stash_triggered()
{
doGitCommand([&](GitPtr g){
g->stash();
});
}
void MainWindow::on_action_stash_apply_triggered()
{
doGitCommand([&](GitPtr g){
g->stash_apply();
});
}
void MainWindow::on_action_stash_drop_triggered()
{
doGitCommand([&](GitPtr g){
g->stash_drop();
});
}
void MainWindow::on_action_online_triggered()
{
ui->radioButton_remote_online->click();
}
void MainWindow::on_action_offline_triggered()
{
ui->radioButton_remote_offline->click();
}
void MainWindow::on_action_repositories_panel_triggered()
{
bool checked = ui->action_repositories_panel->isChecked();
ui->stackedWidget_leftpanel->setCurrentWidget(checked ? ui->page_repos : ui->page_collapsed);
if (checked) {
ui->stackedWidget_leftpanel->setFixedWidth(m->repos_panel_width);
ui->stackedWidget_leftpanel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
ui->stackedWidget_leftpanel->setMinimumWidth(QWIDGETSIZE_MAX);
ui->stackedWidget_leftpanel->setMaximumWidth(QWIDGETSIZE_MAX);
} else {
m->repos_panel_width = ui->stackedWidget_leftpanel->width();
ui->stackedWidget_leftpanel->setFixedWidth(24);
}
}
void MainWindow::on_action_test_triggered()
{
}
diff --git a/src/darktheme/DarkStyle.cpp b/src/darktheme/DarkStyle.cpp
index 195d6ec..749aa70 100644
--- a/src/darktheme/DarkStyle.cpp
+++ b/src/darktheme/DarkStyle.cpp
@@ -1,2496 +1,2521 @@
#include "DarkStyle.h"
#include "NinePatch.h"
#include "TraditionalWindowsStyleTreeControl.h"
#include <QApplication>
#include <QComboBox>
#include <QDebug>
#include <QDialogButtonBox>
#include <QDockWidget>
#include <QInputDialog>
#include <QListView>
#include <QMessageBox>
#include <QPixmapCache>
#include <QStyleOptionComplex>
#include <QStyleOptionFrameV3>
#include <QTableWidget>
#include <QTextLayout>
#include <QToolTip>
#include <cmath>
#include <cstdint>
#define MBI_NORMAL 1
#define MBI_HOT 2
#define MBI_PUSHED 3
#define MBI_DISABLED 4
namespace {
const int windowsItemFrame = 2; // menu item frame width
const int windowsItemHMargin = 3; // menu item hor text margin
const int windowsItemVMargin = 4; // menu item ver text margin
const int windowsArrowHMargin = 6; // arrow horizontal margin
const int windowsRightBorder = 15; // right border on windows
void drawFrame(QPainter *pr, int x, int y, int w, int h, QColor color_topleft, QColor color_bottomright)
{
if (w < 3 || h < 3) {
if (w > 0 && h > 0) {
pr->fillRect(x, y, w, h, color_topleft);
}
} else {
if (!color_topleft.isValid()) color_topleft = Qt::black;
if (!color_bottomright.isValid()) color_bottomright = color_topleft;
pr->fillRect(x, y, w - 1, 1, color_topleft);
pr->fillRect(x, y + 1, 1, h -1, color_topleft);
pr->fillRect(x + w - 1, y, 1, h -1, color_bottomright);
pr->fillRect(x + 1, y + h - 1, w - 1, 1, color_bottomright);
}
}
void drawFrame(QPainter *pr, QRect const &r, QColor const &color_topleft, QColor const &color_bottomright)
{
return drawFrame(pr, r.x(), r.y(), r.width(), r.height(), color_topleft, color_bottomright);
}
struct TextureCacheItem {
QString key;
QPixmap pm;
};
QString pixmapkey(QString const &name, QString const &role, QSize const &size, QColor const &color)
{
QString key = "%1:%2:%3:%4:%5";
key = key.arg(name).arg(role).arg(size.width()).arg(size.height()).arg(QString().sprintf("%02X%02X%02X", color.red(), color.green(), color.blue()));
return key;
}
void drawRaisedFrame(QPainter *p, const QRect &rect, const QPalette &palette)
{
p->save();
int x = rect.x();
int y = rect.y();
int w = rect.width();
int h = rect.height();
p->setClipRect(x, y, w, h);
p->fillRect(x, y, w, h, palette.color(QPalette::Window));
p->fillRect(x, y, w - 1, 1, palette.color(QPalette::Light));
p->fillRect(x, y, 1, h - 1, palette.color(QPalette::Light));
// p->fillRect(x + 1, y + h - 2, w - 2, 1, palette.color(QPalette::Dark));
// p->fillRect(x + w - 2, y + 1, 1, h - 2, palette.color(QPalette::Dark));
p->fillRect(x, y + h - 1, w, 1, palette.color(QPalette::Shadow));
p->fillRect(x + w - 1, y, 1, h, palette.color(QPalette::Shadow));
p->restore();
}
QImage loadImage(QString const &path, QString const &role = QString())
{
QImage image(path);
image.setText("name", path);
image.setText("role", role);
return image;
}
QRgb colorize(QRgb color, int light, int alpha)
{
int r, g, b;
r = g = b = 0;
if (alpha != 0) {
r = qRed(color);
g = qGreen(color);
b = qBlue(color);
if (light < 128) {
r = r * light / 127;
g = g * light / 127;
b = b * light / 127;
} else {
int u = 255 - light;
r = 255 - (255 - r) * u / 127;
g = 255 - (255 - g) * u / 127;
b = 255 - (255 - b) * u / 127;
}
}
return qRgba(r, g, b, alpha);
}
} // namespace
// DarkStyle
static const int TEXTURE_CACHE_SIZE = 100;
struct DarkStyle::Private {
QColor base_color;
bool images_loaded = false;
ScrollBarTextures hsb;
ScrollBarTextures vsb;
int scroll_bar_extent = -1;
QImage button_normal;
QImage button_press;
QImage progress_horz;
QImage progress_vert;
TraditionalWindowsStyleTreeControl legacy_windows;
};
DarkStyle::DarkStyle(QColor const &base_color)
: m(new Private)
{
setBaseColor(base_color);
}
DarkStyle::~DarkStyle()
{
delete m;
}
QColor DarkStyle::getBaseColor()
{
return m->base_color;
}
void DarkStyle::setBaseColor(QColor const &color)
{
m->base_color = color;
m->images_loaded = false;
}
QColor DarkStyle::color(int level, int alpha) const
{
QColor c = m->base_color.lighter(level * 100 / 255);
c.setAlpha(alpha);
return c;
}
void DarkStyle::setScrollBarExtent(int n)
{
m->scroll_bar_extent = n;
}
QImage DarkStyle::colorizeImage(QImage image)
{
int w = image.width();
int h = image.height();
if (w > 0 && h > 0) {
QColor c = color(128);
QRgb rgb = c.rgb();
Q_ASSERT(image.format() == QImage::Format_ARGB32);
for (int y = 0; y < h; y++) {
QRgb *p = reinterpret_cast<QRgb *>(image.scanLine(y));
for (int x = 0; x < w; x++) {
p[x] = colorize(rgb, qGray(p[x]), qAlpha(p[x]));
}
}
}
return image;
}
QImage DarkStyle::loadColorizedImage(QString const &path, QString const &role)
{
QImage image = loadImage(path);
image = colorizeImage(image);
image.setText("name", path);
image.setText("role", role);
return image;
}
namespace {
class Lighten {
private:
int lut[256];
public:
Lighten()
{
#if 0
const double x = 0.75;
for (int i = 0; i < 256; i++) {
lut[i] = (int)(pow(i / 255.0, x) * 255);
}
#else
for (int i = 0; i < 256; i++) {
lut[i] = 255 - (255 - i) * 192 / 256;
}
#endif
}
int operator [] (int i)
{
return lut[i];
}
};
}
DarkStyle::ButtonImages DarkStyle::generateButtonImages(QString const &path)
{
QImage source = loadImage(path);
ButtonImages buttons;
int w = source.width();
int h = source.height();
if (w > 4 && h > 4) {
QColor c = color(128);
QRgb rgb = c.rgb();
Lighten lighten;
buttons.im_normal = source;
buttons.im_hover = source;
w -= 4;
h -= 4;
for (int y = 0; y < h; y++) {
QRgb *src1 = reinterpret_cast<QRgb *>(source.scanLine(y + 1));
QRgb *src2 = reinterpret_cast<QRgb *>(source.scanLine(y + 2));
QRgb *src3 = reinterpret_cast<QRgb *>(source.scanLine(y + 3));
QRgb *dst0 = reinterpret_cast<QRgb *>(buttons.im_normal.scanLine(y + 2)) + 2;
QRgb *dst1 = reinterpret_cast<QRgb *>(buttons.im_hover.scanLine(y + 2)) + 2;
for (int x = 0; x < w; x++) {
int v = (int)qAlpha(src3[x + 3]) - (int)qAlpha(src1[x + 1]);
v = (v + 256) / 2;
int alpha = qAlpha(src2[x + 2]);
dst0[x] = colorize(rgb, v, alpha);
v = lighten[v];
dst1[x] = colorize(rgb, v, alpha);
}
}
buttons.im_normal.setText("name", source.text("name"));
buttons.im_hover.setText("name", source.text("name"));
buttons.im_normal.setText("role", "normal");
buttons.im_hover.setText("role", "hover");
}
return buttons;
}
QImage DarkStyle::generateHoverImage(QImage const &source)
{
QImage newimage;
int w = source.width();
int h = source.height();
if (w > 2 && h > 2) {
QColor c = color(128);
QRgb rgb = c.rgb();
Lighten lighten;
newimage = source;
Q_ASSERT(newimage.format() == QImage::Format_ARGB32);
w -= 2;
h -= 2;
for (int y = 0; y < h; y++) {
QRgb *ptr = reinterpret_cast<QRgb *>(newimage.scanLine(y + 1)) + 1;
for (int x = 0; x < w; x++) {
int v = qGray(ptr[x]);
v = lighten[v];
ptr[x] = colorize(rgb, v, qAlpha(ptr[x]));
}
}
newimage.setText("role", "hover");
}
return newimage;
}
void DarkStyle::loadImages()
{
if (m->images_loaded) return;
if (!m->base_color.isValid()) {
setBaseColor(Qt::white);
}
m->button_normal = loadColorizedImage(QLatin1String(":/darktheme/button/button_normal.png"), QLatin1String("normal"));
m->button_press = loadColorizedImage(QLatin1String(":/darktheme/button/button_press.png"), QLatin1String("press"));
m->hsb.sub_line = generateButtonImages(QLatin1String(":/darktheme/hsb/hsb_sub_line.png"));
m->hsb.add_line = generateButtonImages(QLatin1String(":/darktheme/hsb/hsb_add_line.png"));
m->hsb.page_bg = loadColorizedImage(QLatin1String(":/darktheme/hsb/hsb_page_bg.png"));
m->hsb.slider.im_normal = loadColorizedImage(QLatin1String(":/darktheme/hsb/hsb_slider.png"));
m->hsb.slider.im_hover = generateHoverImage(m->hsb.slider.im_normal);
m->vsb.sub_line = generateButtonImages(QLatin1String(":/darktheme/vsb/vsb_sub_line.png"));
m->vsb.add_line = generateButtonImages(QLatin1String(":/darktheme/vsb/vsb_add_line.png"));
m->vsb.page_bg = loadColorizedImage(QLatin1String(":/darktheme/vsb/vsb_page_bg.png"));
m->vsb.slider.im_normal = loadColorizedImage(QLatin1String(":/darktheme/vsb/vsb_slider.png"));
m->vsb.slider.im_hover = generateHoverImage(m->vsb.slider.im_normal);
m->progress_horz = loadImage(QLatin1String(":/darktheme/progress/horz.png"));
m->progress_vert = loadImage(QLatin1String(":/darktheme/progress/vert.png"));
m->images_loaded = true;
}
QPixmap DarkStyle::pixmapFromImage(const QImage &image, QSize size) const
{
QString key = pixmapkey(image.text("name"), image.text("role"), size, m->base_color);
QPixmap *pm = QPixmapCache::find(key);
if (pm) return *pm;
TextureCacheItem t;
t.key = key;
t.pm = QPixmap::fromImage(image.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
QPixmapCache::insert(key, t.pm);
return t.pm;
}
QColor DarkStyle::selectionColor() const
{
// return QColor(128, 192, 255);
return QColor(80, 160, 255);
}
QColor DarkStyle::colorForItemView(QStyleOption const *opt) const
{
#ifdef Q_OS_WIN
return opt->palette.color(QPalette::Base);
#else
return opt->palette.color(QPalette::Dark);
#endif
}
void DarkStyle::drawNinePatchImage(QPainter *p, const QImage &image, const QRect &r, int w, int h) const
{
QImage im = createImageFromNinePatchImage(image, w, h);
im.setText("name", image.text("name"));
im.setText("role", image.text("role"));
QPixmap pm = pixmapFromImage(im, r.size());
p->drawPixmap(r.x(), r.y(), pm);
}
void DarkStyle::polish(QPalette &palette)
{
if (!m->base_color.isValid()) {
setBaseColor(Qt::white);
}
loadImages();
palette = QPalette(color(64));
#ifndef Q_OS_WIN
palette.setColor(QPalette::ToolTipText, Qt::black); // ツールチップの文字色
#endif
}
void DarkStyle::drawGutter(QPainter *p, const QRect &r) const
{
int x = r.x();
int y = r.y();
int w = r.width();
int h = r.height();
QColor dark = color(32);
QColor lite = color(128);
if (w < h) {
x += (w - 1) / 2;
p->fillRect(x, y, 1, h, dark);
p->fillRect(x + 1, y, 1, h, lite);
} else if (w > h) {
y += (h - 1) / 2;
p->fillRect(x, y, w, 1, dark);
p->fillRect(x, y + 1, w, 1, lite);
}
}
void DarkStyle::drawSelectedItemFrame(QPainter *p, QRect rect, bool focus) const
{
QColor color = focus ? selectionColor() : QColor(128, 128, 128);
int x, y, w, h;
x = rect.x();
y = rect.y();
w = rect.width();
h = rect.height();
QString key = QString::asprintf("selection_frame:%02x%02x%02x:%dx%d", color.red(), color.green(), color.blue(), w, h);
QPixmap pixmap;
if (!QPixmapCache::find(key, &pixmap)) {
pixmap = QPixmap(w, h);
pixmap.fill(Qt::transparent);
QPainter pr(&pixmap);
pr.setRenderHint(QPainter::Antialiasing);
QColor pencolor = color;
pencolor.setAlpha(128);
pr.setPen(pencolor);
pr.setBrush(Qt::NoBrush);
QPainterPath path;
path.addRoundedRect(1.5, 1.5, w - 1.5, h - 1.5, 3, 3);
pr.drawPath(path);
pr.setClipPath(path);
int a = color.alpha();
QColor color0 = color;
QColor color1 = color;
color0.setAlpha(128 * a / 255);
color1.setAlpha(64 * a / 255);
QLinearGradient gr(QPointF(0, 0), QPointF(0, h));
gr.setColorAt(0, color0);
gr.setColorAt(1, color1);
QBrush br(gr);
pr.fillRect(0, 0, w, h, br);
pr.end();
QPixmapCache::insert(key, pixmap);
}
p->drawPixmap(x, y, w, h, pixmap);
}
void DarkStyle::drawFocusFrame(QPainter *p, QRect const &rect, int margin) const
{
p->save();
p->setRenderHint(QPainter::Antialiasing);
QColor color = selectionColor();
p->setPen(color);
p->setBrush(Qt::NoBrush);
double m = margin + 0.5;
p->drawRoundedRect(((QRectF)rect).adjusted(m, m, -m, -m), 3, 3);
p->restore();
}
void DarkStyle::drawButton(QPainter *p, const QStyleOption *option, bool mac_margin) const
{
QRect rect = option->rect;
int w = rect.width();
int h = rect.height();
#ifdef Q_OS_MAC
if (mac_margin) {
int margin = pixelMetric(PM_ButtonMargin, option, nullptr);
if (margin > 0) {
int n = std::min(w, h);
if (n > margin * 2) {
n = (n - margin * 2) / 2;
if (n > margin) n = margin;
rect = rect.adjusted(n, n, -n, -n);
w = rect.width();
h = rect.height();
}
}
}
#else
(void)mac_margin;
#endif
bool pressed = (option->state & (State_Sunken | State_On));
bool hover = (option->state & State_MouseOver);
if (pressed) {
drawNinePatchImage(p, m->button_press, rect, w, h);
} else {
drawNinePatchImage(p, m->button_normal, rect, w, h);
}
{
QPainterPath path;
path.addRoundedRect(rect, 6, 6); // 角丸四角形のパス
p->save();
p->setRenderHint(QPainter::Antialiasing);
p->setClipPath(path);
int x = rect.x();
int y = rect.y();
int w = rect.width();
int h = rect.height();
QColor color0, color1;
#ifdef Q_OS_MAC
if (pressed) {
color0 = Qt::black;
color1 = Qt::black;
color0.setAlpha(48);
color1.setAlpha(144);
} else {
color0 = Qt::black;
color1 = Qt::black;
color0.setAlpha(32);
color1.setAlpha(128);
}
#else
if (pressed) {
color0 = Qt::black;
color1 = Qt::black;
color0.setAlpha(32);
color1.setAlpha(128);
} else if (hover) {
color0 = Qt::black;
color1 = Qt::black;
color0.setAlpha(16);
color1.setAlpha(96);
} else {
color0 = Qt::black;
color1 = Qt::black;
color0.setAlpha(32);
color1.setAlpha(128);
}
#endif
QLinearGradient gr(QPointF(x, y), QPointF(x, y + h));
gr.setColorAt(0, color0);
gr.setColorAt(1, color1);
QBrush br(gr);
p->fillRect(x, y, w, h, br);
if (option->state & State_HasFocus) {
#if 1
drawFocusFrame(p, rect, 3);
#else
p->fillRect(x, y, w, h, QColor(80, 160, 255, 32));
#endif
}
p->restore();
}
}
void DarkStyle::drawToolButton(QPainter *p, const QStyleOption *option) const
{
p->save();
bool hover = (option->state & State_MouseOver);
bool pressed = (option->state & State_Sunken);
#ifdef Q_OS_MAC
hover = false;
#endif
int x = option->rect.x();
int y = option->rect.y();
int w = option->rect.width();
int h = option->rect.height();
QColor color0, color1;
#ifdef Q_OS_MAC
if (pressed) {
color0 = color(72);
color1 = color(40);
} else {
color0 = color(80);
color1 = color(48);
}
#else
if (pressed) {
color0 = color(80);
color1 = color(48);
} else if (hover) {
color0 = color(96);
color1 = color(64);
} else {
color0 = color(80);
color1 = color(48);
}
#endif
QLinearGradient gr(QPointF(x, y), QPointF(x, y + h));
gr.setColorAt(0, color0);
gr.setColorAt(1, color1);
QBrush br(gr);
p->fillRect(x, y, w, h, br);
if (option->state & State_Raised) {
drawFrame(p, option->rect, color(96), color(32));
} else if (option->state & State_Sunken) {
drawFrame(p, option->rect, color(48), color(48));
}
p->restore();
}
void DarkStyle::drawMenuBarBG(QPainter *p, const QStyleOption *option, QWidget const *widget) const
{
int x = option->rect.x();
int y = widget->y();
int w = option->rect.width();
int h = widget->height();
QLinearGradient gradient;
gradient.setStart(x, y);
gradient.setFinalStop(x, y + h / 2.0);
gradient.setColorAt(0, option->palette.color(QPalette::Light));
gradient.setColorAt(1, option->palette.color(QPalette::Window));
p->fillRect(x, y, w, h, gradient);
p->fillRect(x, y + h - 1, w, 1, option->palette.color(QPalette::Dark));
}
int DarkStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
{
if (metric == PM_ScrollBarExtent) {
if (m->scroll_bar_extent > 0) {
return m->scroll_bar_extent;
}
}
if (metric == PM_SliderLength) {
return std::min(widget->width(), widget->height());
}
return QProxyStyle::pixelMetric(metric, option, widget);
}
QRect DarkStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option, SubControl sc, const QWidget *widget) const
{
if (cc == CC_Slider && sc == SC_SliderGroove) {
return widget->rect();
}
if (cc == CC_Slider && sc == SC_SliderHandle) {
if (auto const *o = qstyleoption_cast<QStyleOptionSlider const *>(option)) {
QRect rect;
int extent = std::min(widget->width(), widget->height());
int span = pixelMetric(PM_SliderLength, o, widget);
bool horizontal = o->orientation == Qt::Horizontal;
int sliderPos = sliderPositionFromValue(o->minimum, o->maximum,
o->sliderPosition,
(horizontal ? o->rect.width()
: o->rect.height()) - span,
o->upsideDown);
if (horizontal) {
rect.setRect(o->rect.x() + sliderPos, o->rect.y(), span, extent);
} else {
rect.setRect(o->rect.x(), o->rect.y() + sliderPos, extent, span);
}
return rect;
}
}
if (cc == CC_GroupBox) {
QRect ret;
if (auto const *o = qstyleoption_cast<QStyleOptionGroupBox const *>(option)) {
switch (sc) {
case SC_GroupBoxFrame:
case SC_GroupBoxContents:
{
int topMargin = 0;
int topHeight = 0;
int verticalAlignment = proxy()->styleHint(SH_GroupBox_TextLabelVerticalAlignment, o, widget);
#ifdef Q_OS_MACX
verticalAlignment |= Qt::AlignVCenter;
#endif
if (o->text.size() || (o->subControls & QStyle::SC_GroupBoxCheckBox)) {
topHeight = o->fontMetrics.height();
if (verticalAlignment & Qt::AlignVCenter) {
topMargin = topHeight / 2;
} else if (verticalAlignment & Qt::AlignTop) {
topMargin = topHeight;
}
}
QRect frameRect = o->rect;
frameRect.setTop(topMargin);
if (sc == SC_GroupBoxFrame) {
ret = frameRect;
break;
}
int frameWidth = 0;
if ((o->features & QStyleOptionFrame::Flat) == 0) {
frameWidth = proxy()->pixelMetric(PM_DefaultFrameWidth, o, widget);
}
ret = frameRect.adjusted(frameWidth, frameWidth + topHeight - topMargin, -frameWidth, -frameWidth);
}
break;
case SC_GroupBoxCheckBox:
case SC_GroupBoxLabel:
{
QFontMetrics fontMetrics = o->fontMetrics;
int h = fontMetrics.height();
int tw = fontMetrics.size(Qt::TextShowMnemonic, o->text + QLatin1Char(' ')).width();
int marg = (o->features & QStyleOptionFrame::Flat) ? 0 : 8;
ret = o->rect.adjusted(marg, 0, -marg, 0);
ret.setHeight(h);
int indicatorWidth = proxy()->pixelMetric(PM_IndicatorWidth, option, widget);
int indicatorSpace = proxy()->pixelMetric(PM_CheckBoxLabelSpacing, option, widget) - 1;
bool hasCheckBox = o->subControls & QStyle::SC_GroupBoxCheckBox;
int checkBoxSize = hasCheckBox ? (indicatorWidth + indicatorSpace) : 0;
// Adjusted rect for label + indicatorWidth + indicatorSpace
QRect totalRect = alignedRect(o->direction, o->textAlignment, QSize(tw + checkBoxSize, h), ret);
// Adjust totalRect if checkbox is set
if (hasCheckBox) {
bool ltr = o->direction == Qt::LeftToRight;
int left = 0;
// Adjust for check box
if (sc == SC_GroupBoxCheckBox) {
int indicatorHeight = proxy()->pixelMetric(PM_IndicatorHeight, option, widget);
left = ltr ? totalRect.left() : (totalRect.right() - indicatorWidth);
int top = totalRect.top() + qMax(0, fontMetrics.height() - indicatorHeight) / 2;
totalRect.setRect(left, top, indicatorWidth, indicatorHeight);
// Adjust for label
} else {
left = ltr ? (totalRect.left() + checkBoxSize - 2) : totalRect.left();
totalRect.setRect(left, totalRect.top(), totalRect.width() - checkBoxSize, totalRect.height());
}
}
ret = totalRect;
}
break;
}
return ret;
}
}
return QProxyStyle::subControlRect(cc, option, sc, widget);
}
#if 0
static QSizeF viewItemTextLayout(QTextLayout &textLayout, int lineWidth)
{
qreal height = 0;
qreal widthUsed = 0;
textLayout.beginLayout();
while (true) {
QTextLine line = textLayout.createLine();
if (!line.isValid())
break;
line.setLineWidth(lineWidth);
line.setPosition(QPointF(0, height));
height += line.height();
widthUsed = qMax(widthUsed, line.naturalTextWidth());
}
textLayout.endLayout();
return QSizeF(widthUsed, height);
}
void DarkStyle::viewItemDrawText(QPainter *p, const QStyleOptionViewItem *option, const QRect &rect) const
{
const QWidget *widget = option->widget;
const int textMargin = pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1;
QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding
const bool wrapText = option->features & QStyleOptionViewItem::WrapText;
QTextOption textOption;
textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap);
textOption.setTextDirection(option->direction);
textOption.setAlignment(QStyle::visualAlignment(option->direction, option->displayAlignment));
QTextLayout textLayout(option->text, option->font);
textLayout.setTextOption(textOption);
viewItemTextLayout(textLayout, textRect.width());
const QRectF boundingRect = textLayout.boundingRect();
const QRect layoutRect = QStyle::alignedRect(option->direction, option->displayAlignment, boundingRect.size().toSize(), textRect);
const QPointF position = layoutRect.topLeft();
const int lineCount = textLayout.lineCount();
qreal height = 0;
for (int i = 0; i < lineCount; ++i) {
const QTextLine line = textLayout.lineAt(i);
height += line.height();
// above visible rect
if (height + layoutRect.top() <= textRect.top()) continue;
const int start = line.textStart();
const int length = line.textLength();
const bool drawElided = line.naturalTextWidth() > textRect.width();
bool elideLastVisibleLine = false;
if (!drawElided && i + 1 < lineCount) {
const QTextLine nextLine = textLayout.lineAt(i + 1);
const int nextHeight = height + nextLine.height() / 2;
// elide when less than the next half line is visible
if (nextHeight + layoutRect.top() > textRect.height() + textRect.top()) {
elideLastVisibleLine = true;
}
}
if (drawElided || elideLastVisibleLine) {
QString text = textLayout.text().mid(start, length);
if (elideLastVisibleLine) {
text += QChar(0x2026);
}
// const QStackTextEngine engine(text, option->font);
const QString elidedText = text;//engine.elidedText(option->textElideMode, textRect.width());
const QPointF pos(position.x() + line.x(), position.y() + line.y() + line.ascent());
p->save();
p->setFont(option->font);
p->drawText(pos, elidedText);
p->restore();
} else {
line.draw(p, position);
}
// below visible text, can stop
if (height + layoutRect.top() >= textRect.bottom()) {
break;
}
}
}
#else
void DarkStyle::viewItemDrawText(QPainter *p, const QStyleOptionViewItem *option, const QRect &rect) const
{
bool enabled = (option->state & State_Enabled);
p->save();
p->setFont(option->font);
drawItemText(p, rect, option->displayAlignment, option->palette, enabled, option->text, QPalette::NoRole);
p->restore();
}
#endif
void DarkStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *option, QPainter *p, const QWidget *widget) const
{
// qDebug() << pe;
#ifndef Q_OS_MAC
if (pe == PE_FrameFocusRect) {
// if (auto const *w = qobject_cast<QTableView const *>(widget)) {
// if (w->selectionBehavior() == QAbstractItemView::SelectRows) {
// if (auto const *v = w->viewport()) {
// QRect r(0, option->rect.y(), v->width(), option->rect.height());
// return;
// }
// }
// }
drawFocusFrame(p, option->rect, 0);
return;
}
#endif
if (pe == PE_IndicatorArrowDown) {
switch (pe) {
case PE_IndicatorArrowUp:
case PE_IndicatorArrowDown:
case PE_IndicatorArrowRight:
case PE_IndicatorArrowLeft:
{
if (option->rect.width() <= 1 || option->rect.height() <= 1) {
break;
}
QRect r = option->rect.adjusted(-10, -10, 0, 10);
int size = qMin(r.height(), r.width());
QPixmap pixmap;
if (1) {
int border = size / 5;
int sqsize = 2 * (size / 2);
QImage image(sqsize, sqsize, QImage::Format_ARGB32_Premultiplied);
image.fill(0);
QPainter imagePainter(&image);
QPolygon a;
switch (pe) {
case PE_IndicatorArrowUp:
a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize - border, sqsize / 2);
break;
case PE_IndicatorArrowDown:
a.setPoints(3, border, sqsize / 2, sqsize / 2, sqsize - border, sqsize - border, sqsize / 2);
break;
case PE_IndicatorArrowRight:
a.setPoints(3, sqsize - border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border);
break;
case PE_IndicatorArrowLeft:
a.setPoints(3, border, sqsize / 2, sqsize / 2, border, sqsize / 2, sqsize - border);
break;
default:
break;
}
int bsx = 0;
int bsy = 0;
if (option->state & State_Sunken) {
bsx = pixelMetric(PM_ButtonShiftHorizontal, option, widget);
bsy = pixelMetric(PM_ButtonShiftVertical, option, widget);
}
QRect bounds = a.boundingRect();
int sx = sqsize / 2 - bounds.center().x() - 1;
int sy = sqsize / 2 - bounds.center().y() - 1;
imagePainter.translate(sx + bsx, sy + bsy);
imagePainter.setPen(option->palette.buttonText().color());
imagePainter.setBrush(option->palette.buttonText());
imagePainter.setRenderHint(QPainter::Qt4CompatiblePainting);
if (!(option->state & State_Enabled)) {
imagePainter.translate(1, 1);
imagePainter.setBrush(option->palette.light().color());
imagePainter.setPen(option->palette.light().color());
imagePainter.drawPolygon(a);
imagePainter.translate(-1, -1);
imagePainter.setBrush(option->palette.mid().color());
imagePainter.setPen(option->palette.mid().color());
}
imagePainter.drawPolygon(a);
imagePainter.end();
pixmap = QPixmap::fromImage(image);
}
int xOffset = r.x() + (r.width() - size)/2;
int yOffset = r.y() + (r.height() - size)/2;
p->drawPixmap(xOffset, yOffset, pixmap);
}
}
return;
}
if (pe == PE_PanelMenu) {
QRect r = option->rect;
drawFrame(p, r, Qt::black, Qt::black);
r = r.adjusted(1, 1, -1, -1);
drawFrame(p, r, color(128), color(64));
r = r.adjusted(1, 1, -1, -1);
p->fillRect(r, color(80));
return;
}
if (pe == PE_FrameMenu) {
return;
}
if (pe == PE_PanelButtonBevel) {
drawButton(p, option);
return;
}
if (pe == PE_PanelStatusBar) {
p->fillRect(option->rect, option->palette.color(QPalette::Window));
return;
}
if (pe == PE_FrameTabWidget) {
if (auto const *o = qstyleoption_cast<QStyleOptionTabWidgetFrame const *>(option)) {
int x = o->rect.x();
int y = o->rect.y();
int w = o->rect.width();
int h = o->rect.height();
#ifdef Q_OS_WIN
switch (o->shape) {
case QTabBar::RoundedNorth:
break;
case QTabBar::RoundedSouth:
h -= 1;
break;
case QTabBar::RoundedWest:
break;
case QTabBar::RoundedEast:
w -= 2;
break;
}
#endif
drawRaisedFrame(p, QRect(x, y, w, h), o->palette);
return;
}
}
if (pe == PE_PanelLineEdit) {
if (auto const *panel = qstyleoption_cast<QStyleOptionFrame const *>(option)) {
p->fillRect(option->rect, colorForItemView(option));
if (panel->lineWidth > 0) {
drawFrame(p, option->rect, option->palette.color(QPalette::Shadow), option->palette.color(QPalette::Light));
}
}
return;
}
if (pe == PE_FrameGroupBox) {
p->save();
p->setRenderHint(QPainter::Antialiasing);
QRectF r = option->rect;
r = r.adjusted(1.5, 1.5, -0.5, -0.5);
p->setPen(option->palette.color(QPalette::Light));
p->drawRoundedRect(r, 5, 5);
r = r.adjusted(-1, -1, -1, -1);
p->setPen(option->palette.color(QPalette::Dark));
p->drawRoundedRect(r, 5, 5);
p->restore();
return;
}
if (pe == PE_PanelItemViewRow) {
// p->drawEllipse(option->rect);
// return;
#ifdef Q_OS_WIN
// thru
#else
return;
#endif
}
if (pe == PE_PanelItemViewItem) {
// p->fillRect(option->rect, colorForItemView(option)); // 選択枠を透過描画させるので背景は描かない
auto DrawSelectionFrame = [&](QRect const &r){
bool focus = widget && widget->hasFocus();
drawSelectedItemFrame(p, r, focus);
};
if (auto const *tableview = qobject_cast<QTableView const *>(widget)) {
Qt::PenStyle grid_pen_style = Qt::NoPen;
if (tableview->showGrid()) {
grid_pen_style = tableview->gridStyle();
}
QAbstractItemView::SelectionBehavior selection_behavior = tableview->selectionBehavior();
if (grid_pen_style != Qt::NoPen) {
int x = option->rect.x();
int y = option->rect.y();
int w = option->rect.width();
int h = option->rect.height();
p->fillRect(x + w - 1, y, 1, h, option->palette.color(QPalette::Dark));
p->fillRect(x, y + h - 1, w, 1, option->palette.color(QPalette::Dark));
}
if (option->state & State_Selected) {
p->save();
p->setClipRect(option->rect);
QRect r = widget->rect();
if (selection_behavior == QAbstractItemView::SelectionBehavior::SelectRows) {
r = QRect(r.x(), option->rect.y(), r.width(), option->rect.height());
} else if (selection_behavior == QAbstractItemView::SelectionBehavior::SelectRows) {
r = QRect(option->rect.x(), r.y(), option->rect.y(), r.height());
}
DrawSelectionFrame(r);
p->restore();
}
} else {
if (option->state & State_Selected) {
DrawSelectionFrame(option->rect);
}
}
return;
}
if (pe == QStyle::PE_IndicatorBranch) {
QColor bg = option->palette.color(QPalette::Base);
p->fillRect(option->rect, bg);
if (m->legacy_windows.drawPrimitive(pe, option, p, widget)) {
return;
}
}
if (pe == QStyle::PE_Widget) { // bg for messagebox
const QDialogButtonBox *buttonBox = nullptr;
if (qobject_cast<const QMessageBox *> (widget))
buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_msgbox_buttonbox"));
#ifndef QT_NO_INPUTDIALOG
else if (qobject_cast<const QInputDialog *> (widget))
buttonBox = widget->findChild<const QDialogButtonBox *>(QLatin1String("qt_inputdlg_buttonbox"));
#endif // QT_NO_INPUTDIALOG
if (buttonBox) {
int y = buttonBox->geometry().top();
QRect r(option->rect.x(), y, option->rect.width(), 1);
p->fillRect(r, option->palette.color(QPalette::Light));
r.translate(0, -1);
p->fillRect(r, option->palette.color(QPalette::Dark));
}
return;
}
#ifndef Q_OS_WIN
if (pe == QStyle::PE_PanelTipLabel) {
// ツールチップの背景パネル
p->fillRect(option->rect, QColor(255, 255, 192));
drawFrame(p, option->rect, Qt::black, Qt::black);
return;
}
#endif
// qDebug() << pe;
QProxyStyle::drawPrimitive(pe, option, p, widget);
}
void DarkStyle::drawControl(ControlElement ce, const QStyleOption *option, QPainter *p, const QWidget *widget) const
{
// qDebug() << ce;
bool disabled = !(option->state & State_Enabled);
#ifdef Q_OS_MAC
if (ce == CE_ToolBar) {
int x = option->rect.x();
int y = option->rect.y();
int w = option->rect.width();
int h = option->rect.height();
p->fillRect(x, y + h - 1, w, 1, color(32));
return;
}
#endif
#ifdef Q_OS_LINUX
if (ce == CE_ToolBar) {
int x = option->rect.x();
int y = option->rect.y();
int w = option->rect.width();
int h = option->rect.height();
QColor color = option->palette.color(QPalette::Window);
p->fillRect(x, y + h - 1, w, 1, color);
return;
}
if (ce == CE_PushButtonLabel) {
if (auto const *o = qstyleoption_cast<QStyleOptionButton const *>(option)) {
QRect ir = o->rect;
uint tf = Qt::AlignVCenter | Qt::TextShowMnemonic;
QPoint buttonShift;
if (option->state & State_Sunken) {
buttonShift = QPoint(pixelMetric(PM_ButtonShiftHorizontal, option, widget), proxy()->pixelMetric(PM_ButtonShiftVertical, option, widget));
}
if (proxy()->styleHint(SH_UnderlineShortcut, o, widget)) {
tf |= Qt::TextShowMnemonic;
} else {
tf |= Qt::TextHideMnemonic;
}
if (!o->icon.isNull()) {
//Center both icon and text
QPoint point;
QIcon::Mode mode = o->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
if (mode == QIcon::Normal && o->state & State_HasFocus) {
mode = QIcon::Active;
}
QIcon::State state = QIcon::Off;
if (o->state & State_On) {
state = QIcon::On;
}
QPixmap pixmap = o->icon.pixmap(o->iconSize, mode, state);
int w = pixmap.width();
int h = pixmap.height();
if (!o->text.isEmpty()) {
w += o->fontMetrics.boundingRect(option->rect, tf, o->text).width() + 4;
}
point = QPoint(ir.x() + ir.width() / 2 - w / 2, ir.y() + ir.height() / 2 - h / 2);
if (o->direction == Qt::RightToLeft) {
point.rx() += pixmap.width();
}
p->drawPixmap(visualPos(o->direction, o->rect, point + buttonShift), pixmap);
if (o->direction == Qt::RightToLeft) {
ir.translate(-point.x() - 2, 0);
} else {
ir.translate(point.x() + pixmap.width() + 2, 0);
}
// left-align text if there is
if (!o->text.isEmpty()) {
tf |= Qt::AlignLeft;
}
} else {
tf |= Qt::AlignHCenter;
}
ir.translate(buttonShift);
if (o->features & QStyleOptionButton::HasMenu) {
ir = ir.adjusted(0, 0, -pixelMetric(PM_MenuButtonIndicator, o, widget), 0);
}
drawItemText(p, ir, tf, option->palette, (o->state & State_Enabled), o->text, QPalette::ButtonText);
return;
}
}
if (ce == CE_RadioButton || ce == CE_CheckBox) {
if (auto const *o = qstyleoption_cast<QStyleOptionButton const *>(option)) {
bool isRadio = (ce == CE_RadioButton);
QStyleOptionButton subopt = *o;
subopt.rect = subElementRect(isRadio ? SE_RadioButtonIndicator : SE_CheckBoxIndicator, o, widget);
proxy()->drawPrimitive(isRadio ? PE_IndicatorRadioButton : PE_IndicatorCheckBox, &subopt, p, widget);
subopt.rect = subElementRect(isRadio ? SE_RadioButtonContents : SE_CheckBoxContents, o, widget);
drawControl(isRadio ? CE_RadioButtonLabel : CE_CheckBoxLabel, &subopt, p, widget);
if (o->state & State_HasFocus) {
QStyleOptionFocusRect fropt;
fropt.QStyleOption::operator=(*o);
fropt.rect = subElementRect(isRadio ? SE_RadioButtonFocusRect : SE_CheckBoxFocusRect, o, widget);
drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
}
return;
}
}
if (ce == CE_RadioButtonLabel || ce == CE_CheckBoxLabel) {
if (auto const *o = qstyleoption_cast<QStyleOptionButton const *>(option)) {
uint alignment = visualAlignment(o->direction, Qt::AlignLeft | Qt::AlignVCenter);
if (!styleHint(SH_UnderlineShortcut, o, widget)) {
alignment |= Qt::TextHideMnemonic;
}
QPixmap pix;
QRect textRect = o->rect;
if (!o->icon.isNull()) {
pix = o->icon.pixmap(o->iconSize, o->state & State_Enabled ? QIcon::Normal : QIcon::Disabled);
drawItemPixmap(p, o->rect, alignment, pix);
if (o->direction == Qt::RightToLeft) {
textRect.setRight(textRect.right() - o->iconSize.width() - 4);
} else {
textRect.setLeft(textRect.left() + o->iconSize.width() + 4);
}
}
if (!o->text.isEmpty()){
drawItemText(p, textRect, alignment | Qt::TextShowMnemonic, o->palette, o->state & State_Enabled, o->text, QPalette::ButtonText);
}
return;
}
}
if (ce == CE_ComboBoxLabel) {
if (auto const *o = qstyleoption_cast<QStyleOptionComboBox const *>(option)) {
QRect editRect = subControlRect(CC_ComboBox, o, SC_ComboBoxEditField, widget);
uint alignment = Qt::AlignLeft | Qt::AlignVCenter;
if (!styleHint(SH_UnderlineShortcut, o, widget)) {
alignment |= Qt::TextHideMnemonic;
}
QPixmap pix;
QRect iconRect(editRect);
QIcon icon = o->currentIcon;
QString text = o->currentText;
if (!icon.isNull()) {
pix = icon.pixmap(o->iconSize, o->state & State_Enabled ? QIcon::Normal : QIcon::Disabled);
drawItemPixmap(p, iconRect, alignment, pix);
if (o->direction == Qt::RightToLeft) {
editRect.setRight(editRect.right() - o->iconSize.width() - 4);
} else {
editRect.setLeft(editRect.left() + o->iconSize.width() + 4);
}
}
if (!text.isEmpty()){
drawItemText(p, editRect, alignment, o->palette, o->state & State_Enabled, text, QPalette::ButtonText);
}
return;
}
}
#endif
if (ce == CE_ShapedFrame) {
if (auto const *o = qstyleoption_cast<QStyleOptionFrameV3 const *>(option)) {
int lw = o->lineWidth;
if (lw > 0) {
QRect r = o->rect;
if (o->frameShape == QFrame::Panel || o->frameShape == QFrame::HLine || o->frameShape == QFrame::VLine) {
if (o->state & (State_Sunken | State_Raised)) {
if (r.width() < r.height()) {
if (r.width() > 2) {
r.adjust(0, 0, -1, 0);
}
} else {
if (r.height() > 2) {
r.adjust(0, 0, 0, -1);
}
}
int x = r.x();
int y = r.y();
int w = r.width();
int h = r.height();
QColor color_topleft = o->palette.color(QPalette::Dark);
QColor color_bottomright = o->palette.color(QPalette::Light);
if (o->state & State_Raised) {
std::swap(color_topleft, color_bottomright);
}
int t = lw;
if (o->frameShape == QFrame::HLine) {
y = h / 2 - t;
h = t * 2;
} else if (o->frameShape == QFrame::VLine) {
x = w / 2 - t;
w = t * 2;
}
p->fillRect(x, y, w - t, t, color_topleft);
p->fillRect(x, y + t, t, h - t, color_topleft);
p->fillRect(x + w - t, y, t, h - t, color_bottomright);
p->fillRect(x + t, y + h - t, w - t, t, color_bottomright);
} else {
if (r.width() < r.height()) {
if (lw < r.width()) {
r = QRect(r.x() + (r.width() - lw) / 2, r.y(), lw, r.height());
}
} else {
if (lw < r.height()) {
r = QRect(r.x(), r.y() + (r.height() - lw) / 2, r.width(), lw);
}
}
int x = r.x();
int y = r.y();
int w = r.width();
int h = r.height();
QColor color = o->palette.color(QPalette::Shadow);
p->fillRect(x, y, w, h, color);
}
} else if (o->frameShape == QFrame::Box) {
int x = r.x();
int y = r.y();
int w = r.width();
int h = r.height();
if (w > 1) w--;
if (h > 1) h--;
QColor color_topleft = o->palette.color(QPalette::Dark);
QColor color_bottomright = o->palette.color(QPalette::Light);
if (o->state & State_Raised) {
std::swap(color_topleft, color_bottomright);
}
drawFrame(p, x + 1, y + 1, w, h, color_bottomright, color_bottomright);
drawFrame(p, x, y, w, h, color_topleft, color_topleft);
} else if (o->frameShape == QFrame::StyledPanel) {
QColor color = o->palette.color(QPalette::Midlight);
int x = r.x();
int y = r.y();
int w = r.width();
int h = r.height();
drawFrame(p, x, y, w, h, color, color);
}
}
}
return;
}
if (ce == CE_PushButtonBevel) {
if (auto const *o = qstyleoption_cast<QStyleOptionButton const *>(option)) {
if (o->features & QStyleOptionButton::Flat) {
// nop
} else {
drawButton(p, option);
}
if (o->features & QStyleOptionButton::HasMenu) {
int mbiw = 0, mbih = 0;
QRect r = subElementRect(SE_PushButtonContents, option, nullptr);
QStyleOptionButton newBtn = *o;
r = QRect(r.right() - mbiw - 2, option->rect.top() + (option->rect.height()/2) - (mbih/2), mbiw + 1, mbih + 1);
newBtn.rect = QStyle::visualRect(option->direction, option->rect, r);
drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
}
}
return;
}
if (ce == CE_MenuBarEmptyArea) {
drawMenuBarBG(p, option, widget);
return;
}
if (ce == CE_MenuBarItem) {
drawMenuBarBG(p, option, widget);
if (option->state & State_Selected) {
drawSelectedItemFrame(p, option->rect, true);
}
if (auto const *o = qstyleoption_cast<QStyleOptionMenuItem const *>(option)) {
QPalette::ColorRole textRole = disabled ? QPalette::Text : QPalette::ButtonText;
QPixmap pix = o->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), QIcon::Normal);
uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
if (!styleHint(SH_UnderlineShortcut, o, widget)) {
alignment |= Qt::TextHideMnemonic;
}
if (!pix.isNull()) {
drawItemPixmap(p, o->rect, alignment, pix);
} else {
drawItemText(p, o->rect, alignment, o->palette, o->state & State_Enabled, o->text, textRole);
}
}
return;
}
if (ce == CE_MenuEmptyArea) {
return;
}
if (ce == CE_MenuItem) {
if (auto const *o = qstyleoption_cast<QStyleOptionMenuItem const *>(option)) {
+#ifdef Q_OS_MAC
+ int checkcol = 15;
+#else
// windows always has a check column, regardless whether we have an icon or not
int checkcol = 25;// / QWindowsXPStylePrivate::devicePixelRatio(widget);
+#endif
const int gutterWidth = 3;// / QWindowsXPStylePrivate::devicePixelRatio(widget);
QRect rect = option->rect;
bool ignoreCheckMark = false;
if (qobject_cast<const QComboBox*>(widget)) {
{ // bg
int x = option->rect.x();
int y = option->rect.y();
int w = option->rect.width();
int h = option->rect.height();
p->fillRect(x, y, w, h, option->palette.color(QPalette::Light));
}
ignoreCheckMark = true; //ignore the checkmarks provided by the QComboMenuDelegate
}
//draw vertical menu line
if (option->direction == Qt::LeftToRight) {
checkcol += rect.x();
}
int x, y, w, h;
o->rect.getRect(&x, &y, &w, &h);
int tab = o->tabWidth;
bool dis = !(o->state & State_Enabled);
bool checked = (o->checkType != QStyleOptionMenuItem::NotCheckable) && o->checked;
bool act = o->state & State_Selected;
if (o->menuItemType == QStyleOptionMenuItem::Separator) {
QRect r = option->rect.adjusted(2, 0, -2, 0);
drawGutter(p, r);
return;
}
+#ifdef Q_OS_MAC
+// qDebug() << pixelMetric(PM_SmallIconSize, option, widget);
+ QRect vCheckRect = visualRect(option->direction, o->rect, QRect(o->rect.x(), o->rect.y(), 20 - (gutterWidth + o->rect.x()), o->rect.height()));
+#else
QRect vCheckRect = visualRect(option->direction, o->rect, QRect(o->rect.x(), o->rect.y(), checkcol - (gutterWidth + o->rect.x()), o->rect.height()));
+#endif
if (act) {
drawSelectedItemFrame(p, option->rect, true);
}
+ int xm = windowsItemFrame + checkcol + windowsItemHMargin + (gutterWidth - o->rect.x()) - 1;
+ int xpos = o->rect.x() + xm;
+
if (checked && !ignoreCheckMark) {
const qreal boxMargin = 3.5;
const qreal boxWidth = checkcol - 2 * boxMargin;
const int checkColHOffset = windowsItemHMargin + windowsItemFrame - 1;
- QRectF checkRectF(option->rect.left() + boxMargin + checkColHOffset, option->rect.center().y() - boxWidth/2 + 1, boxWidth, boxWidth);
+#ifdef Q_OS_MAC
+ QRectF checkRectF(0, option->rect.y(), xpos, option->rect.height());
+ QRect checkRect = checkRectF.toRect();
+ checkRect.setWidth(checkRect.height());
+ checkRect.adjust(windowsItemFrame + (xpos - windowsItemFrame - checkRect.width()) / 2, 0, 0, 0);
+#else
+ QRectF checkRectF(option->rect.left() + boxMargin + checkColHOffset, option->rect.center().y() - boxWidth / 2 + 1, boxWidth, boxWidth);
QRect checkRect = checkRectF.toRect();
checkRect.setWidth(checkRect.height()); // avoid .toRect() round error results in non-perfect square
+#endif
checkRect = visualRect(o->direction, o->rect, checkRect);
QStyleOptionButton box;
box.QStyleOption::operator=(*option);
box.rect = checkRect;
if (checked) {
box.state |= State_On;
}
drawPrimitive(PE_IndicatorCheckBox, &box, p, widget);
}
+
if (!ignoreCheckMark) {
if (!o->icon.isNull()) {
QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal;
if (act && !dis) {
mode = QIcon::Active;
}
QPixmap pixmap;
if (checked) {
pixmap = o->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), mode, QIcon::On);
} else {
pixmap = o->icon.pixmap(pixelMetric(PM_SmallIconSize, option, widget), mode);
}
double dpr = pixmap.devicePixelRatio();
if (dpr > 0) {
const int pixw = int(pixmap.width() / dpr);
const int pixh = int(pixmap.height() / dpr);
+#ifdef Q_OS_MAC
+ QRect pmr(xpos, vCheckRect.y() + (vCheckRect.height() - pixh) / 2, pixw, pixh);
+ p->setPen(o->palette.text().color());
+ p->drawPixmap(pmr.topLeft(), pixmap);
+ xpos += pmr.width() + 4;
+#else
QRect pmr(0, 0, pixw, pixh);
pmr.moveCenter(vCheckRect.center());
p->setPen(o->palette.text().color());
p->drawPixmap(pmr.topLeft(), pixmap);
+#endif
}
}
} else {
if (o->icon.isNull()) {
checkcol = 0;
} else {
checkcol = o->maxIconWidth;
}
}
p->setPen(o->palette.buttonText().color());
const QColor textColor = o->palette.text().color();
if (dis) {
p->setPen(textColor);
}
- int xm = windowsItemFrame + checkcol + windowsItemHMargin + (gutterWidth - o->rect.x()) - 1;
- int xpos = o->rect.x() + xm;
QRect textRect(xpos, y + windowsItemVMargin, w - xm - windowsRightBorder - tab + 1, h - 2 * windowsItemVMargin);
QRect vTextRect = visualRect(option->direction, o->rect, textRect);
QString s = o->text;
if (!s.isEmpty()) { // draw text
p->save();
int t = s.indexOf(QLatin1Char('\t'));
int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine;
if (!styleHint(SH_UnderlineShortcut, o, widget)) {
text_flags |= Qt::TextHideMnemonic;
}
text_flags |= Qt::AlignLeft;
if (t >= 0) {
QRect vShortcutRect = visualRect(option->direction, o->rect, QRect(textRect.topRight(), QPoint(o->rect.right(), textRect.bottom())));
p->drawText(vShortcutRect, text_flags, s.mid(t + 1));
s = s.left(t);
}
QFont font = o->font;
if (o->menuItemType == QStyleOptionMenuItem::DefaultItem) {
font.setBold(true);
}
p->setFont(font);
p->setPen(textColor);
p->drawText(vTextRect, text_flags, s.left(t));
p->restore();
}
if (o->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow
int dim = (h - 2 * windowsItemFrame) / 2;
PrimitiveElement arrow;
arrow = (option->direction == Qt::RightToLeft) ? PE_IndicatorArrowLeft : PE_IndicatorArrowRight;
xpos = x + w - windowsArrowHMargin - windowsItemFrame - dim;
QRect vSubMenuRect = visualRect(option->direction, o->rect, QRect(xpos, y + h / 2 - dim / 2, dim, dim));
QStyleOptionMenuItem newMI = *o;
newMI.rect = vSubMenuRect;
newMI.state = dis ? State_None : State_Enabled;
drawPrimitive(arrow, &newMI, p, widget);
}
}
return;
}
if (ce == CE_TabBarTabShape) {
#ifdef Q_OS_MAC
if (auto const *o = qstyleoption_cast<QStyleOptionTab const *>(option)) {
drawButton(p, option, false);
bool selected = o->state & State_Selected;
if (selected) {
drawSelectedItemFrame(p, o->rect.adjusted(0, 0, -2, -2), widget);
}
return;
}
#else
if (auto const *o = qstyleoption_cast<QStyleOptionTab const *>(option)) {
bool rtlHorTabs = (o->direction == Qt::RightToLeft && (o->shape == QTabBar::RoundedNorth || o->shape == QTabBar::RoundedSouth));
bool selected = o->state & State_Selected;
bool lastTab = ((!rtlHorTabs && o->position == QStyleOptionTab::End) || (rtlHorTabs && o->position == QStyleOptionTab::Beginning));
bool firstTab = ((!rtlHorTabs && o->position == QStyleOptionTab::Beginning) || (rtlHorTabs && o->position == QStyleOptionTab::End));
bool onlyOne = o->position == QStyleOptionTab::OnlyOneTab;
bool previousSelected = ((!rtlHorTabs && o->selectedPosition == QStyleOptionTab::PreviousIsSelected) || (rtlHorTabs && o->selectedPosition == QStyleOptionTab::NextIsSelected));
bool nextSelected = ((!rtlHorTabs && o->selectedPosition == QStyleOptionTab::NextIsSelected) || (rtlHorTabs && o->selectedPosition == QStyleOptionTab::PreviousIsSelected));
int tabBarAlignment = proxy()->styleHint(SH_TabBar_Alignment, o, widget);
bool leftAligned = (!rtlHorTabs && tabBarAlignment == Qt::AlignLeft) || (rtlHorTabs && tabBarAlignment == Qt::AlignRight);
bool rightAligned = (!rtlHorTabs && tabBarAlignment == Qt::AlignRight) || (rtlHorTabs && tabBarAlignment == Qt::AlignLeft);
QColor light = o->palette.light().color();
// QColor dark = o->palette.dark().color();
QColor shadow = o->palette.shadow().color();
int borderThinkness = proxy()->pixelMetric(PM_TabBarBaseOverlap, o, widget);
if (selected) {
borderThinkness /= 2;
}
QRect r2(option->rect);
int x1 = r2.left();
int x2 = r2.right();
int y1 = r2.top();
int y2 = r2.bottom();
switch (o->shape) {
default:
QProxyStyle::drawControl(ce, o, p, widget);
break;
case QTabBar::RoundedNorth:
{
#ifdef Q_OS_WIN
if (selected) {
y2 += 2;
} else {
y2 += 1;
y1 += 2;
x1 += onlyOne || firstTab ? borderThinkness : 0;
x2 -= onlyOne || lastTab ? borderThinkness : 0;
}
#elif 1
if (selected) {
y2 += 2;
} else {
y2 += 1;
y1 += 2;
x1 += onlyOne || firstTab ? borderThinkness : 0;
x2 -= onlyOne || lastTab ? borderThinkness : 0;
}
#else
if (!selected) {
y1 += 2;
x1 += onlyOne || firstTab ? borderThinkness : 0;
x2 -= onlyOne || lastTab ? borderThinkness : 0;
}
#endif
p->fillRect(QRect(x1 + 1, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 2), o->palette.background());
// Left
if (firstTab || selected || onlyOne || !previousSelected) {
p->setPen(light);
p->drawLine(x1, y1 + 2, x1, y2 - 1 - ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness));
p->drawPoint(x1 + 1, y1 + 1);
}
// Top
{
int beg = x1 + (previousSelected ? 0 : 2);
int end = x2 - (nextSelected ? 0 : 2);
p->setPen(light);
p->drawLine(beg, y1, end, y1);
}
// Right
if (lastTab || selected || onlyOne || !nextSelected) {
p->setPen(shadow);
p->drawLine(x2, y1 + 2, x2, y2 - 1 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness));
p->drawPoint(x2 - 1, y1 + 1);
}
break;
}
case QTabBar::RoundedSouth:
{
#ifdef Q_OS_WIN
if (selected) {
y1 -= 2;
} else {
x1 -= 1;
y1 -= 1;
y2 -= 2;
x1 += firstTab ? borderThinkness : 0;
x2 -= lastTab ? borderThinkness : 0;
}
#elif 1
if (selected) {
y1 -= 2;
} else {
y1 -= 1;
y2 -= 2;
x1 += firstTab ? borderThinkness : 0;
x2 -= lastTab ? borderThinkness : 0;
}
#else
if (!selected) {
y2 -= 2;
x1 += firstTab ? borderThinkness : 0;
x2 -= lastTab ? borderThinkness : 0;
}
#endif
p->fillRect(QRect(x1 + 1, y1 + 2, (x2 - x1) - 1, (y2 - y1) - 1), o->palette.background());
// Left
if (firstTab || selected || onlyOne || !previousSelected) {
p->setPen(light);
p->drawLine(x1, y2 - 2, x1, y1 + ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness));
p->drawPoint(x1 + 1, y2 - 1);
}
// Bottom
{
int beg = x1 + (previousSelected ? 0 : 2);
int end = x2 - (nextSelected ? 0 : 2);
p->setPen(shadow);
p->drawLine(beg, y2, end, y2);
}
// Right
if (lastTab || selected || onlyOne || !nextSelected) {
p->setPen(shadow);
p->drawLine(x2, y2 - 2, x2, y1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness));
p->drawPoint(x2 - 1, y2 - 1);
}
break; }
case QTabBar::RoundedWest:
{
#ifdef Q_OS_WIN
if (selected) {
x2 += 1;
} else {
x1 += 2;
y1 += firstTab ? borderThinkness : 0;
y2 -= lastTab ? borderThinkness : 0;
}
#elif 1
if (selected) {
x2 += 1;
} else {
x1 += 2;
y1 += firstTab ? borderThinkness : 0;
y2 -= lastTab ? borderThinkness : 0;
}
#else
if (!selected) {
x1 += 2;
y1 += firstTab ? borderThinkness : 0;
y2 -= lastTab ? borderThinkness : 0;
}
#endif
p->fillRect(QRect(x1 + 1, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 1), o->palette.background());
// Top
if (firstTab || selected || onlyOne || !previousSelected) {
p->setPen(light);
p->drawLine(x1 + 2, y1, x2 - ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness), y1);
p->drawPoint(x1 + 1, y1 + 1);
}
// Left
{
int beg = y1 + (previousSelected ? 0 : 2);
int end = y2 - (nextSelected ? 0 : 2);
p->setPen(light);
p->drawLine(x1, beg, x1, end);
}
// Bottom
if (lastTab || selected || onlyOne || !nextSelected) {
p->setPen(shadow);
p->drawLine(x1 + 2, y2, x2 - ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2);
p->drawPoint(x1 + 1, y2 - 1);
}
break;
}
case QTabBar::RoundedEast:
{
#ifdef Q_OS_WIN
if (selected) {
x1 -= 2;
} else {
y1 -= 2;
x1 -= 1;
x2 -= 2;
y1 += firstTab ? borderThinkness : 0;
y2 -= lastTab ? borderThinkness : 0;
}
#elif 1
if (selected) {
x1 -= 2;
} else {
x1 -= 1;
x2 -= 2;
y1 += firstTab ? borderThinkness : 0;
y2 -= lastTab ? borderThinkness : 0;
}
#else
if (!selected) {
x2 -= 2;
y1 += firstTab ? borderThinkness : 0;
y2 -= lastTab ? borderThinkness : 0;
}
#endif
p->fillRect(QRect(x1 + 2, y1 + 1, (x2 - x1) - 1, (y2 - y1) - 1), o->palette.background());
// Top
if (firstTab || selected || onlyOne || !previousSelected) {
p->setPen(light);
p->drawLine(x2 - 2, y1, x1 - 1 + ((onlyOne || firstTab) && selected && leftAligned ? 0 : borderThinkness), y1);
p->drawPoint(x2 - 1, y1 + 1);
}
// Right
{
int beg = y1 + (previousSelected ? 0 : 2);
int end = y2 - (nextSelected ? 0 : 2);
p->setPen(shadow);
p->drawLine(x2, beg, x2, end);
}
// Bottom
if (lastTab || selected || onlyOne || !nextSelected) {
p->setPen(shadow);
p->drawLine(x2 - 2, y2, x1 - 1 + ((onlyOne || lastTab) && selected && rightAligned ? 0 : borderThinkness), y2);
p->drawPoint(x2 - 1, y2 - 1);
}
break;
}
}
}
#endif
return;
}
if (ce == CE_ProgressBarGroove || ce == CE_ProgressBarContents) {
if (auto const *o = qstyleoption_cast<QStyleOptionProgressBar const *>(option)) {
QColor color(0, 128, 255);
bool horz = (o->orientation == Qt::Horizontal);
QString key;
QImage const *image;
if (horz) {
key = "horz";
image = &m->progress_horz;
} else {
key = "vert";
image = &m->progress_vert;
}
p->save();
int x = option->rect.x();
int y = option->rect.y();
int w = option->rect.width();
int h = option->rect.height();
if (ce == CE_ProgressBarContents) {
int len = o->progress - o->minimum;
int div = o->maximum - o->minimum;
bool inv = o->invertedAppearance;
if (horz) {
int v = div > 0 ? (w * len / div) : 0;
if (inv) {
x = x + w - v;
}
w = v;
} else {
int v = div > 0 ? (h * len / div) : 0;
if (!inv) {
y = y + h - v;
}
h = v;
}
QRect r(x, y, w, h);
p->setClipRect(r);
p->fillRect(option->rect, color);
}
QPixmap pm;
QSize size(w, h);
key = pixmapkey("progress_bar", key, size, m->base_color);
if (!QPixmapCache::find(key, &pm)) {
QImage im = image->scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
pm = QPixmap::fromImage(im);
QPixmapCache::insert(key, pm);
}
p->setOpacity(0.5);
p->drawPixmap(option->rect, pm, pm.rect());
p->restore();
}
return;
}
if (ce == CE_HeaderSection || ce == CE_HeaderEmptyArea) {
bool horz = true;
if (auto const *o = qstyleoption_cast<QStyleOptionHeader const *>(option)) {
horz = (o->orientation == Qt::Horizontal);
}
int x = option->rect.x();
int y = option->rect.y();
int w = option->rect.width();
int h = option->rect.height();
QLinearGradient gradient;
gradient.setStart(x, y);
gradient.setFinalStop(x, y + h / 4.0);
gradient.setColorAt(0, option->palette.color(QPalette::Light));
gradient.setColorAt(1, option->palette.color(QPalette::Window));
p->fillRect(x, y, w, h, gradient);
p->fillRect(x, y + h - 1, w, 1, option->palette.color(QPalette::Dark));
if (horz) {
p->fillRect(x + w - 1, y, 1, h, option->palette.color(QPalette::Dark));
}
if (ce == CE_HeaderSection) {
if (horz) {
p->fillRect(x, y, 1, h, option->palette.color(QPalette::Light));
}
if (option->state & QStyle::State_MouseOver) {
p->save();
p->fillRect(x, y, w, h, QColor(255, 255, 255, 32));
p->restore();
}
}
if (option->state & QStyle::State_Sunken) {
drawFocusFrame(p, option->rect.adjusted(0, 0, -1, -1), 0);
}
return;
}
if (ce == CE_HeaderLabel) {
if (auto const *o = qstyleoption_cast<QStyleOptionHeader const *>(option)) {
QRect rect = o->rect;
if (!o->icon.isNull()) {
int iconExtent = pixelMetric(PM_SmallIconSize, option, widget);
QPixmap pixmap = o->icon.pixmap(QSize(iconExtent, iconExtent), (o->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled);
int pixw = int(pixmap.width() / pixmap.devicePixelRatio());
QRect aligned = alignedRect(o->direction, QFlag(o->iconAlignment), pixmap.size() / pixmap.devicePixelRatio(), rect);
QRect inter = aligned.intersected(rect);
p->drawPixmap(inter.x(), inter.y(), pixmap, inter.x() - aligned.x(), inter.y() - aligned.y(), int(aligned.width() * pixmap.devicePixelRatio()), int(pixmap.height() * pixmap.devicePixelRatio()));
const int margin = pixelMetric(QStyle::PM_HeaderMargin, option, widget);
if (o->direction == Qt::LeftToRight) {
rect.setLeft(rect.left() + pixw + margin);
} else {
rect.setRight(rect.right() - pixw - margin);
}
}
if (o->state & QStyle::State_On) {
QFont fnt = p->font();
fnt.setBold(true);
p->setFont(fnt);
}
int leftmargin = 0;
auto align = o->textAlignment;
if (align & Qt::AlignLeft) {
leftmargin += 4;
}
drawItemText(p, rect.adjusted(leftmargin, 0, 0, 0), align, o->palette, (o->state & State_Enabled), o->text, QPalette::ButtonText);
}
return;
}
#ifdef Q_OS_MAC
if (ce == CE_DockWidgetTitle) {
if (auto const *o = qstyleoption_cast<QStyleOptionDockWidget const *>(option)) {
auto const *dockWidget = qobject_cast<QDockWidget const *>(widget);
QRect rect = option->rect;
if (dockWidget && dockWidget->isFloating()) {
QProxyStyle::drawControl(ce, option, p, widget);
return;
}
auto const *v2 = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(o);
bool verticalTitleBar = v2 && v2->verticalTitleBar;
if (verticalTitleBar) {
rect.setSize(rect.size().transposed());
p->translate(rect.left() - 1, rect.top() + rect.width());
p->rotate(-90);
p->translate(-rect.left() + 1, -rect.top());
}
p->setBrush(option->palette.background().color().darker(110));
p->setPen(option->palette.background().color().darker(130));
p->drawRect(rect.adjusted(0, 1, -1, -3));
int buttonMargin = 4;
int mw = proxy()->pixelMetric(QStyle::PM_DockWidgetTitleMargin, o, widget);
int fw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, o, widget);
auto const *dw = qobject_cast<const QDockWidget *>(widget);
bool isFloating = dw && dw->isFloating();
QRect r = option->rect.adjusted(0, 2, -1, -3);
QRect titleRect = r;
if (o->closable) {
QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarCloseButton, o, widget).actualSize(QSize(10, 10));
titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
}
if (o->floatable) {
QSize sz = proxy()->standardIcon(QStyle::SP_TitleBarMaxButton, o, widget).actualSize(QSize(10, 10));
titleRect.adjust(0, 0, -sz.width() - mw - buttonMargin, 0);
}
if (isFloating) {
titleRect.adjust(0, -fw, 0, 0);
if (widget && widget->windowIcon().cacheKey() != QApplication::windowIcon().cacheKey()) {
titleRect.adjust(titleRect.height() + mw, 0, 0, 0);
}
} else {
titleRect.adjust(mw, 0, 0, 0);
if (!o->floatable && !o->closable) {
titleRect.adjust(0, 0, -mw, 0);
}
}
if (!verticalTitleBar) {
titleRect = visualRect(o->direction, r, titleRect);
}
if (!o->title.isEmpty()) {
QString titleText = p->fontMetrics().elidedText(o->title, Qt::ElideRight, verticalTitleBar ? titleRect.height() : titleRect.width());
const int indent = 4;
int align = Qt::AlignRight | Qt::AlignVCenter;
drawItemText(p, rect.adjusted(indent + 1, 1, -indent - 1, -1), align, o->palette, o->state & State_Enabled, titleText, QPalette::WindowText);
}
}
return;
}
#endif // Q_OS_MAC
#ifdef Q_OS_LINUX
if (ce == CE_Splitter) {
p->fillRect(option->rect, option->palette.color(QPalette::Window));
return;
}
#endif
if (ce == CE_ItemViewItem) {
#if 0
if (auto const *o = qstyleoption_cast<QStyleOptionViewItem const *>(option)) {
p->save();
p->setFont(o->font);
drawPrimitive(PE_PanelItemViewItem, option, p, widget);
drawItemText(p, o->rect.adjusted(2, 0, 0, 0), o->displayAlignment, option->palette, true, o->text);
p->restore();
return;
}
#else
if (auto const *o = qstyleoption_cast<QStyleOptionViewItem const *>(option)) {
p->save();
p->setClipRect(o->rect);
QRect checkRect = subElementRect(SE_ItemViewItemCheckIndicator, o, widget);
QRect iconRect = subElementRect(SE_ItemViewItemDecoration, o, widget);
QRect textRect = subElementRect(SE_ItemViewItemText, o, widget);
textRect.adjust(2, 0, 0, 0);
// draw the background
drawPrimitive(PE_PanelItemViewItem, o, p, widget);
// draw the check mark
if (o->features & QStyleOptionViewItem::HasCheckIndicator) {
QStyleOptionViewItem o2(*o);
o2.rect = checkRect;
o2.state = o2.state & ~QStyle::State_HasFocus;
switch (o->checkState) {
case Qt::Unchecked:
o2.state |= QStyle::State_Off;
break;
case Qt::PartiallyChecked:
o2.state |= QStyle::State_NoChange;
break;
case Qt::Checked:
o2.state |= QStyle::State_On;
break;
}
drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &o2, p, widget);
}
// draw the icon
QIcon::Mode mode = QIcon::Normal;
if (!(o->state & QStyle::State_Enabled)) {
mode = QIcon::Disabled;
} else if (o->state & QStyle::State_Selected) {
mode = QIcon::Selected;
}
QIcon::State state = o->state & QStyle::State_Open ? QIcon::On : QIcon::Off;
o->icon.paint(p, iconRect, o->decorationAlignment, mode, state);
// draw the text
if (!o->text.isEmpty()) {
QPalette::ColorGroup cg = o->state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
if (cg == QPalette::Normal && !(o->state & QStyle::State_Active)) {
cg = QPalette::Inactive;
}
if (o->state & QStyle::State_Selected) {
p->setPen(o->palette.color(cg, QPalette::HighlightedText));
} else {
p->setPen(o->palette.color(cg, QPalette::Text));
}
if (o->state & QStyle::State_Editing) {
p->setPen(o->palette.color(cg, QPalette::Text));
p->drawRect(textRect.adjusted(0, 0, -1, -1));
}
viewItemDrawText(p, o, textRect);
}
#if 0
// draw the focus rect
if (o->state & QStyle::State_HasFocus) {
QStyleOptionFocusRect o3;
o3.QStyleOption::operator=(*o);
o3.rect = subElementRect(SE_ItemViewItemFocusRect, o, widget);
o3.state |= QStyle::State_KeyboardFocusChange;
o3.state |= QStyle::State_Item;
QPalette::ColorGroup cg = (o->state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled;
o3.backgroundColor = o->palette.color(cg, (o->state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Window);
drawPrimitive(PE_FrameFocusRect, &o3, p, widget);
}
#endif
p->restore();
}
return;
#endif
}
if (ce == CE_Header) {
drawControl(CE_HeaderSection, option, p, widget);
drawControl(CE_HeaderLabel, option, p, widget);
return;
}
if (ce == CE_TabBarTab) {
drawControl(CE_TabBarTabShape, option, p, widget);
drawControl(CE_TabBarTabLabel, option, p, widget);
return;
}
if (ce == CE_PushButton) {
drawControl(CE_PushButtonBevel, option, p, widget);
drawControl(CE_PushButtonLabel, option, p, widget);
return;
}
if (ce == CE_ProgressBar) {
drawControl(CE_ProgressBarGroove, option, p, widget);
drawControl(CE_ProgressBarContents, option, p, widget);
return;
}
// qDebug() << ce;
QProxyStyle::drawControl(ce, option, p, widget);
}
void DarkStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *option, QPainter *p, const QWidget *widget) const
{
// qDebug() << cc;
if (cc == QStyle::CC_ComboBox) {
if (auto const *o = qstyleoption_cast<QStyleOptionComboBox const *>(option)) {
SubControls sub = option->subControls;
if (o->editable) {
#define EP_EDITBORDER_NOSCROLL 6
#define EP_EDITBORDER_HVSCROLL 9
if (sub & SC_ComboBoxEditField) {
p->fillRect(option->rect, option->palette.color(QPalette::Dark));
drawFrame(p, option->rect, option->palette.color(QPalette::Shadow), option->palette.color(QPalette::Light));
}
if (sub & SC_ComboBoxArrow) {
QRect subRect = subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget);
QStyleOptionComplex opt = *option;
opt.rect = subRect;
drawToolButton(p, &opt);
opt.rect = QRect(subRect.right() - 4, subRect.y() + subRect.height() / 2 - 1, 2, 2);
drawPrimitive(PE_IndicatorArrowDown, &opt, p, widget);
}
} else {
if (sub & SC_ComboBoxFrame) {
QStyleOptionButton btn;
btn.QStyleOption::operator=(*option);
btn.rect = option->rect.adjusted(-1, -1, 1, 1);
if (sub & SC_ComboBoxArrow) {
btn.features = QStyleOptionButton::HasMenu;
}
drawControl(QStyle::CE_PushButton, &btn, p, widget);
}
}
}
return;
}
if (cc == QStyle::CC_ToolButton) {
QStyle::State flags = option->state;
if (auto const *o = qstyleoption_cast<QStyleOptionToolButton const *>(option)) {
QRect button, menuarea;
button = subControlRect(cc, o, SC_ToolButton, widget);
menuarea = subControlRect(cc, o, SC_ToolButtonMenu, widget);
State bflags = o->state & ~State_Sunken;
State mflags = bflags;
bool autoRaise = flags & State_AutoRaise;
if (autoRaise) {
if (!(bflags & State_MouseOver) || !(bflags & State_Enabled)) {
bflags &= ~State_Raised;
}
}
if (o->state & State_Sunken) {
if (o->activeSubControls & SC_ToolButton) {
bflags |= State_Sunken;
mflags |= State_MouseOver | State_Sunken;
} else if (o->activeSubControls & SC_ToolButtonMenu) {
mflags |= State_Sunken;
bflags |= State_MouseOver;
}
}
QStyleOption tool(0);
tool.palette = o->palette;
if (o->subControls & SC_ToolButton) {
if (flags & (State_Sunken | State_On | State_Raised) || !autoRaise) {
tool.rect = option->rect;
tool.state = bflags;
drawToolButton(p, &tool);
}
}
if (o->state & State_HasFocus) {
QStyleOptionFocusRect fr;
fr.QStyleOption::operator=(*o);
fr.rect.adjust(3, 3, -3, -3);
if (o->features & QStyleOptionToolButton::MenuButtonPopup) {
fr.rect.adjust(0, 0, -pixelMetric(QStyle::PM_MenuButtonIndicator, o, widget), 0);
}
drawPrimitive(PE_FrameFocusRect, &fr, p, widget);
}
QStyleOptionToolButton label = *o;
label.state = bflags;
int fw = 2;
if (!autoRaise)
label.state &= ~State_Sunken;
label.rect = button.adjusted(fw, fw, -fw, -fw);
drawControl(CE_ToolButtonLabel, &label, p, widget);
if (o->subControls & SC_ToolButtonMenu) {
tool.rect = menuarea;
tool.state = mflags;
if (autoRaise) {
drawPrimitive(PE_IndicatorButtonDropDown, &tool, p, widget);
} else {
tool.state = mflags;
menuarea.adjust(-2, 0, 0, 0);
// Draw menu button
if ((bflags & State_Sunken) != (mflags & State_Sunken)){
p->save();
p->setClipRect(menuarea);
tool.rect = option->rect;
drawToolButton(p, &tool);
p->restore();
}
// Draw arrow
p->save();
p->setPen(option->palette.dark().color());
p->drawLine(menuarea.left(), menuarea.top() + 3, menuarea.left(), menuarea.bottom() - 3);
p->setPen(option->palette.light().color());
p->drawLine(menuarea.left() - 1, menuarea.top() + 3, menuarea.left() - 1, menuarea.bottom() - 3);
tool.rect = menuarea.adjusted(2, 3, -2, -1);
drawPrimitive(PE_IndicatorArrowDown, &tool, p, widget);
p->restore();
}
} else if (o->features & QStyleOptionToolButton::HasMenu) {
int mbi = pixelMetric(PM_MenuButtonIndicator, o, widget);
QRect ir = o->rect;
QStyleOptionToolButton newBtn = *o;
newBtn.rect = QRect(ir.right() + 4 - mbi, ir.height() - mbi + 4, mbi - 5, mbi - 5);
drawPrimitive(PE_IndicatorArrowDown, &newBtn, p, widget);
}
}
return;
}
if (cc == QStyle::CC_ScrollBar) {
bool ishorz = (option->state & State_Horizontal);
auto *tx = const_cast<ScrollBarTextures *>(ishorz ? &m->hsb : &m->vsb);
int extent = (ishorz ? tx->slider.im_normal.height() : tx->slider.im_normal.width()) - 2;
auto DrawNinePatchImage2 = [&](QImage const &image, QRect const &r){
int w, h;
if (ishorz) {
h = extent;
int d = r.height();
w = (d == 0) ? 0 : (h * r.width() / d);
} else {
w = extent;
int d = r.width();
h = (d == 0) ? 0 : (w * r.height() / d);
}
drawNinePatchImage(p, image, r, w, h);
};
p->fillRect(option->rect, color(64));
p->setRenderHint(QPainter::Antialiasing);
{
#ifdef Q_OS_MAC
QRect r = option->rect;
#else
QRect r1 = subControlRect(CC_ScrollBar, option, SC_ScrollBarAddPage, widget);
QRect r2 = subControlRect(CC_ScrollBar, option, SC_ScrollBarSubPage, widget);
QRect r = r1.united(r2);
#endif
DrawNinePatchImage2(tx->page_bg, r);
}
#ifndef Q_OS_MAC // macOSのスクロールバーには、ラインステップボタンが無い
{
QRect r = subControlRect(CC_ScrollBar, option, SC_ScrollBarSubLine, widget);
DrawNinePatchImage2(tx->page_bg, r);
}
{
QRect r = subControlRect(CC_ScrollBar, option, SC_ScrollBarAddLine, widget);
DrawNinePatchImage2(tx->page_bg, r);
}
auto DrawAddSubButton = [&](ButtonImages const &ims, SubControl subctl){
QRect r = subControlRect(CC_ScrollBar, option, subctl, widget);
QPixmap pm;;
if (!(option->activeSubControls & subctl)) {
pm = pixmapFromImage(ims.im_normal, r.size());
p->drawPixmap(r.topLeft(), pm);
} else {
pm = pixmapFromImage(ims.im_hover, r.size());
p->drawPixmap(r.topLeft(), pm);
}
};
DrawAddSubButton(tx->sub_line, SC_ScrollBarSubLine);
DrawAddSubButton(tx->add_line, SC_ScrollBarAddLine);
#endif
{
QRect r = subControlRect(CC_ScrollBar, option, SC_ScrollBarSlider, widget);
int w, h;
if (ishorz) {
h = extent;
int d = r.height();
w = (d == 0) ? 0 : (h * r.width() / d);
} else {
w = extent;
int d = r.width();
h = (d == 0) ? 0 : (w * r.height() / d);
}
#ifdef Q_OS_MAC // macだとズレて見えるので調整する
if (ishorz) {
r = r.translated(1, 0);
} else {
r = r.translated(0, 1);
}
#endif
if (!(option->activeSubControls & SC_ScrollBarSlider)) {
DrawNinePatchImage2(tx->slider.im_normal, r);
} else {
DrawNinePatchImage2(tx->slider.im_hover, r);
}
}
return;
}
if (cc == CC_GroupBox) {
if (auto const *o = qstyleoption_cast<QStyleOptionGroupBox const *>(option)) {
QRect textRect = subControlRect(CC_GroupBox, option, SC_GroupBoxLabel, widget);
QRect checkBoxRect = subControlRect(CC_GroupBox, option, SC_GroupBoxCheckBox, widget);
if (o->subControls & QStyle::SC_GroupBoxFrame) {
QStyleOptionFrame frame;
frame.QStyleOption::operator=(*o);
frame.features = o->features;
frame.lineWidth = o->lineWidth;
frame.midLineWidth = o->midLineWidth;
frame.rect = subControlRect(CC_GroupBox, option, SC_GroupBoxFrame, widget);
p->save();
QRegion region(o->rect);
if (!o->text.isEmpty()) {
bool ltr = o->direction == Qt::LeftToRight;
QRect finalRect;
if (o->subControls & QStyle::SC_GroupBoxCheckBox) {
finalRect = checkBoxRect.united(textRect);
finalRect.adjust(ltr ? -4 : 0, 0, ltr ? 0 : 4, 0);
} else {
finalRect = textRect;
}
region -= finalRect;
}
p->setClipRegion(region);
drawPrimitive(PE_FrameGroupBox, &frame, p, widget);
p->restore();
}
// Draw title
if ((o->subControls & QStyle::SC_GroupBoxLabel) && !o->text.isEmpty()) {
QColor textColor = o->textColor;
if (textColor.isValid()) {
p->setPen(textColor);
}
int alignment = int(o->textAlignment);
if (!styleHint(QStyle::SH_UnderlineShortcut, option, widget)) {
alignment |= Qt::TextHideMnemonic;
}
alignment |= Qt::TextShowMnemonic;
alignment |= Qt::AlignHCenter;
drawItemText(p, textRect, alignment, o->palette, o->state & State_Enabled, o->text, QPalette::WindowText);
if (o->state & State_HasFocus) {
QStyleOptionFocusRect fropt;
fropt.QStyleOption::operator=(*o);
fropt.rect = textRect;
drawPrimitive(PE_FrameFocusRect, &fropt, p, widget);
}
}
// Draw checkbox
if (o->subControls & SC_GroupBoxCheckBox) {
QStyleOptionButton box;
box.QStyleOption::operator=(*o);
box.rect = checkBoxRect;
drawPrimitive(PE_IndicatorCheckBox, &box, p, widget);
}
}
return;
}
if (cc == CC_Slider) {
if (auto const *o = qstyleoption_cast<QStyleOptionSlider const *>(option)) {
QRect groove = subControlRect(CC_Slider, option, SC_SliderGroove, widget);
QRect handle = subControlRect(CC_Slider, option, SC_SliderHandle, widget);
bool horizontal = o->orientation == Qt::Horizontal;
bool ticksAbove = o->tickPosition & QSlider::TicksAbove;
bool ticksBelow = o->tickPosition & QSlider::TicksBelow;
QPixmap cache;
QBrush oldBrush = p->brush();
QPen oldPen = p->pen();
QColor shadowAlpha(Qt::black);
shadowAlpha.setAlpha(10);
if ((option->subControls & SC_SliderGroove) && groove.isValid()) {
QRect rect(0, 0, groove.width(), groove.height());
QString key = pixmapkey("slider_groove", horizontal ? "horz" : "vert", rect.size(), m->base_color);
QRectF grooveRect;
double r = std::min(groove.width(), groove.height()) / 2.0;
{
double n = r * 3 / 4;
grooveRect = QRectF(rect).adjusted(n, n, -n, -n);
r -= n;
}
// draw background groove
QPixmap cache;
if (!QPixmapCache::find(key, cache)) {
cache = QPixmap(rect.size());
cache.fill(Qt::transparent);
QPainter groovePainter(&cache);
groovePainter.setRenderHint(QPainter::Antialiasing, true);
groovePainter.translate(0.5, 0.5);
QLinearGradient gradient;
if (horizontal) {
gradient.setStart(grooveRect.center().x(), grooveRect.top());
gradient.setFinalStop(grooveRect.center().x(), grooveRect.bottom());
} else {
gradient.setStart(grooveRect.left(), grooveRect.center().y());
gradient.setFinalStop(grooveRect.right(), grooveRect.center().y());
}
groovePainter.setPen(Qt::NoPen);
gradient.setColorAt(0, color(32));
gradient.setColorAt(1, color(128));
groovePainter.setBrush(gradient);
groovePainter.drawRoundedRect(grooveRect, r, r);
groovePainter.end();
QPixmapCache::insert(key, cache);
}
p->drawPixmap(groove.topLeft(), cache);
}
if (option->subControls & SC_SliderTickmarks) {
int tickSize = pixelMetric(PM_SliderTickmarkOffset, option, widget);
int available = pixelMetric(PM_SliderSpaceAvailable, o, widget);
int interval = o->tickInterval;
if (interval <= 0) {
interval = o->singleStep;
if (QStyle::sliderPositionFromValue(o->minimum, o->maximum, interval, available) - QStyle::sliderPositionFromValue(o->minimum, o->maximum, 0, available) < 3) {
interval = o->pageStep;
}
}
if (interval <= 0) {
interval = 1;
}
int v = o->minimum;
int len = pixelMetric(PM_SliderLength, o, widget);
while (v <= o->maximum + 1) {
if (v == o->maximum + 1 && interval == 1) break;
const int v2 = qMin(v, o->maximum);
int pos = sliderPositionFromValue(o->minimum, o->maximum,
v2, (horizontal
? o->rect.width()
: o->rect.height()) - len,
o->upsideDown) + len / 2;
int extra = 2 - ((v2 == o->minimum || v2 == o->maximum) ? 1 : 0);
if (horizontal) {
if (ticksAbove) {
p->setPen(color(128));
p->drawLine(pos, o->rect.top() + extra, pos, o->rect.top() + tickSize);
}
if (ticksBelow) {
p->setPen(color(0));
p->drawLine(pos, o->rect.bottom() - extra, pos, o->rect.bottom() - tickSize);
}
} else {
if (ticksAbove) {
p->setPen(color(128));
p->drawLine(o->rect.left() + extra, pos, o->rect.left() + tickSize, pos);
}
if (ticksBelow) {
p->setPen(color(0));
p->drawLine(o->rect.right() - extra, pos, o->rect.right() - tickSize, pos);
}
}
// in the case where maximum is max int
int nextInterval = v + interval;
if (nextInterval < v) break;
v = nextInterval;
}
}
// draw handle
if ((option->subControls & SC_SliderHandle) ) {
QRect pixmapRect(0, 0, handle.width(), handle.height());
QRect r = pixmapRect.adjusted(0, 0, -1, -1);
QPainterPath path;
path.addEllipse(r);
QString handlePixmapName = pixmapkey(QLatin1String("slider_handle"), "", handle.size(), m->base_color);
if (!QPixmapCache::find(handlePixmapName, cache)) {
cache = QPixmap(handle.size());
cache.fill(Qt::transparent);
QPainter handlePainter(&cache);
handlePainter.setRenderHint(QPainter::Antialiasing, true);
handlePainter.translate(0.5, 0.5);
QLinearGradient gradient;
gradient.setStart(pixmapRect.topLeft());
gradient.setFinalStop(pixmapRect.bottomRight());
gradient.setColorAt(0, color(192));
gradient.setColorAt(1, QColor(0, 0, 0));
handlePainter.save();
handlePainter.setClipPath(path);
handlePainter.fillRect(r, gradient);
QColor highlight_color = colorize(color(160).rgb(), 192, 255);
handlePainter.setPen(QPen(highlight_color, 2));
handlePainter.setBrush(Qt::NoBrush);
handlePainter.drawEllipse(r.adjusted(0, 0, 2, 2));
handlePainter.setPen(QPen(QColor(0, 0, 0), 2));
handlePainter.setBrush(Qt::NoBrush);
handlePainter.drawEllipse(r.adjusted(-2, -2, 0, 0));
handlePainter.restore();
handlePainter.end();
QPixmapCache::insert(handlePixmapName, cache);
}
QPoint topleft = handle.topLeft();
p->drawPixmap(topleft, cache);
if ((option->state & State_HasFocus) && (option->state & State_KeyboardFocusChange)) {
p->save();
p->setClipPath(path.translated(topleft));
p->fillRect(handle, QColor(255, 255, 255, 32));
p->restore();
}
}
p->setBrush(oldBrush);
p->setPen(oldPen);
}
return;
}
// qDebug() << cc;
QProxyStyle::drawComplexControl(cc, option, p, widget);
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 17, 9:10 PM (1 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
70288
Default Alt Text
(145 KB)

Event Timeline