mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-28 18:32:50 +00:00
Merge branch 'recent-files-improvements' (fix #578)
This commit is contained in:
commit
f5c0d667c9
@ -654,6 +654,15 @@
|
|||||||
<style id="recent_file_detail" border="2" border-left="0" extends="recent_file">
|
<style id="recent_file_detail" border="2" border-left="0" extends="recent_file">
|
||||||
<text color="disabled" align="left" x="2" />
|
<text color="disabled" align="left" x="2" />
|
||||||
</style>
|
</style>
|
||||||
|
<style id="recent_file_pin">
|
||||||
|
<background color="background" />
|
||||||
|
<background color="menuitem_hot_face" state="mouse" />
|
||||||
|
<background color="listitem_selected_face" state="selected" />
|
||||||
|
<icon part="unpinned" align="center middle" color="text" />
|
||||||
|
<icon part="unpinned" align="center middle" color="listitem_selected_text" state="selected" />
|
||||||
|
<icon part="pinned" align="center middle" color="text" state="focus" />
|
||||||
|
<icon part="pinned" align="center middle" color="listitem_selected_text" state="focus selected" />
|
||||||
|
</style>
|
||||||
<style id="news_item" border="2">
|
<style id="news_item" border="2">
|
||||||
<background color="background" />
|
<background color="background" />
|
||||||
<background color="menuitem_hot_face" state="mouse" />
|
<background color="menuitem_hot_face" state="mouse" />
|
||||||
|
@ -545,6 +545,8 @@ go_up_button_tooltip = Up to parent folder
|
|||||||
new_folder_button_tooltip = New folder
|
new_folder_button_tooltip = New folder
|
||||||
file_name = File name:
|
file_name = File name:
|
||||||
file_type = File type:
|
file_type = File type:
|
||||||
|
pinned_folders = Pinned Folders
|
||||||
|
recent_folders = Recent Folders
|
||||||
|
|
||||||
[filters]
|
[filters]
|
||||||
selected_cels = Selected
|
selected_cels = Selected
|
||||||
|
2
laf
2
laf
@ -1 +1 @@
|
|||||||
Subproject commit 950f62c1ccff7cf59c220e9016ead0845767e546
|
Subproject commit 75f29b27c661e66e1549dc6e6a172f284cd9326b
|
@ -474,17 +474,20 @@ bool AppMenus::rebuildRecentList()
|
|||||||
submenu = new Menu();
|
submenu = new Menu();
|
||||||
list_menuitem->setSubmenu(submenu);
|
list_menuitem->setSubmenu(submenu);
|
||||||
|
|
||||||
auto it = App::instance()->recentFiles()->files_begin();
|
auto recent = App::instance()->recentFiles();
|
||||||
auto end = App::instance()->recentFiles()->files_end();
|
base::paths files;
|
||||||
if (it != end) {
|
files.insert(files.end(),
|
||||||
|
recent->pinnedFiles().begin(),
|
||||||
|
recent->pinnedFiles().end());
|
||||||
|
files.insert(files.end(),
|
||||||
|
recent->recentFiles().begin(),
|
||||||
|
recent->recentFiles().end());
|
||||||
|
if (!files.empty()) {
|
||||||
Params params;
|
Params params;
|
||||||
|
for (const auto& fn : files) {
|
||||||
for (; it != end; ++it) {
|
params.set("filename", fn.c_str());
|
||||||
const char* filename = it->c_str();
|
|
||||||
params.set("filename", filename);
|
|
||||||
|
|
||||||
menuitem = new AppMenuItem(
|
menuitem = new AppMenuItem(
|
||||||
base::get_file_name(filename).c_str(),
|
base::get_file_name(fn).c_str(),
|
||||||
cmd_open_file,
|
cmd_open_file,
|
||||||
params);
|
params);
|
||||||
submenu->addChild(menuitem);
|
submenu->addChild(menuitem);
|
||||||
|
@ -274,4 +274,11 @@ void del_config_value(const char* section, const char* name)
|
|||||||
g_configs.back()->deleteValue(section, name);
|
g_configs.back()->deleteValue(section, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base::paths enum_config_keys(const char* section)
|
||||||
|
{
|
||||||
|
base::paths keys;
|
||||||
|
g_configs.back()->getAllKeys(section, keys);
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "app/color.h"
|
#include "app/color.h"
|
||||||
|
#include "base/paths.h"
|
||||||
#include "gfx/point.h"
|
#include "gfx/point.h"
|
||||||
#include "gfx/rect.h"
|
#include "gfx/rect.h"
|
||||||
#include "gfx/size.h"
|
#include "gfx/size.h"
|
||||||
@ -58,6 +59,8 @@ namespace app {
|
|||||||
|
|
||||||
void del_config_value(const char* section, const char* name);
|
void del_config_value(const char* section, const char* name);
|
||||||
|
|
||||||
|
base::paths enum_config_keys(const char* section);
|
||||||
|
|
||||||
// Generic get/set_config_value functions
|
// Generic get/set_config_value functions
|
||||||
|
|
||||||
inline const char* get_config_value(const char* section, const char* name, const char* value) {
|
inline const char* get_config_value(const char* section, const char* name, const char* value) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2018 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -11,8 +12,8 @@
|
|||||||
#include "app/recent_files.h"
|
#include "app/recent_files.h"
|
||||||
|
|
||||||
#include "app/ini_file.h"
|
#include "app/ini_file.h"
|
||||||
|
#include "base/convert_to.h"
|
||||||
#include "base/fs.h"
|
#include "base/fs.h"
|
||||||
#include "fmt/format.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -20,8 +21,10 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const char* kRecentFilesSection = "RecentFiles";
|
const char* kSectionName[] = { "PinnedFiles",
|
||||||
const char* kRecentFoldersSection = "RecentPaths";
|
"RecentFiles",
|
||||||
|
"PinnedPaths",
|
||||||
|
"RecentPaths" };
|
||||||
|
|
||||||
struct compare_path {
|
struct compare_path {
|
||||||
std::string a;
|
std::string a;
|
||||||
@ -31,81 +34,40 @@ struct compare_path {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string format_filename_var(const int i) {
|
|
||||||
return fmt::format("Filename{0:02d}", i);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string format_folder_var(const int i) {
|
|
||||||
return fmt::format("Path{0:02d}", i);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
RecentFiles::RecentFiles(const int limit)
|
RecentFiles::RecentFiles(const int limit)
|
||||||
: m_files(limit)
|
: m_limit(limit)
|
||||||
, m_paths(limit)
|
|
||||||
{
|
{
|
||||||
for (int c=m_files.limit()-1; c>=0; c--) {
|
load();
|
||||||
const char* filename = get_config_string(kRecentFilesSection,
|
|
||||||
format_filename_var(c).c_str(),
|
|
||||||
nullptr);
|
|
||||||
if (filename && *filename && base::is_file(filename)) {
|
|
||||||
std::string fn = normalizePath(filename);
|
|
||||||
m_files.addItem(fn, compare_path(fn));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int c=m_paths.limit()-1; c>=0; c--) {
|
|
||||||
const char* path = get_config_string(kRecentFoldersSection,
|
|
||||||
format_folder_var(c).c_str(),
|
|
||||||
nullptr);
|
|
||||||
if (path && *path) {
|
|
||||||
std::string p = normalizePath(path);
|
|
||||||
m_paths.addItem(p, compare_path(p));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RecentFiles::~RecentFiles()
|
RecentFiles::~RecentFiles()
|
||||||
{
|
{
|
||||||
// Save recent files
|
save();
|
||||||
|
|
||||||
int c = 0;
|
|
||||||
for (auto const& filename : m_files) {
|
|
||||||
set_config_string(kRecentFilesSection,
|
|
||||||
format_filename_var(c).c_str(),
|
|
||||||
filename.c_str());
|
|
||||||
++c;
|
|
||||||
}
|
|
||||||
for (; c<m_files.limit(); ++c) {
|
|
||||||
del_config_value(kRecentFilesSection,
|
|
||||||
format_filename_var(c).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save recent folders
|
|
||||||
|
|
||||||
c = 0;
|
|
||||||
for (auto const& path : m_paths) {
|
|
||||||
set_config_string(kRecentFoldersSection,
|
|
||||||
format_folder_var(c).c_str(),
|
|
||||||
path.c_str());
|
|
||||||
++c;
|
|
||||||
}
|
|
||||||
for (; c<m_files.limit(); ++c) {
|
|
||||||
del_config_value(kRecentFoldersSection,
|
|
||||||
format_folder_var(c).c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecentFiles::addRecentFile(const std::string& filename)
|
void RecentFiles::addRecentFile(const std::string& filename)
|
||||||
{
|
{
|
||||||
std::string fn = normalizePath(filename);
|
std::string fn = normalizePath(filename);
|
||||||
m_files.addItem(fn, compare_path(fn));
|
|
||||||
|
|
||||||
|
// If the filename is already pinned, we don't add it in the
|
||||||
|
// collection of recent files collection.
|
||||||
|
auto it = std::find(m_paths[kPinnedFiles].begin(),
|
||||||
|
m_paths[kPinnedFiles].end(), fn);
|
||||||
|
if (it != m_paths[kPinnedFiles].end())
|
||||||
|
return;
|
||||||
|
addItem(m_paths[kRecentFiles], fn);
|
||||||
|
|
||||||
|
// Add recent folder
|
||||||
std::string path = base::get_file_path(fn);
|
std::string path = base::get_file_path(fn);
|
||||||
m_paths.addItem(path, compare_path(path));
|
it = std::find(m_paths[kPinnedFolders].begin(),
|
||||||
|
m_paths[kPinnedFolders].end(), path);
|
||||||
|
if (it == m_paths[kPinnedFolders].end()) {
|
||||||
|
addItem(m_paths[kRecentFolders], path);
|
||||||
|
}
|
||||||
|
|
||||||
Changed();
|
Changed();
|
||||||
}
|
}
|
||||||
@ -113,7 +75,7 @@ void RecentFiles::addRecentFile(const std::string& filename)
|
|||||||
void RecentFiles::removeRecentFile(const std::string& filename)
|
void RecentFiles::removeRecentFile(const std::string& filename)
|
||||||
{
|
{
|
||||||
std::string fn = normalizePath(filename);
|
std::string fn = normalizePath(filename);
|
||||||
m_files.removeItem(fn, compare_path(fn));
|
removeItem(m_paths[kRecentFiles], fn);
|
||||||
|
|
||||||
std::string dir = base::get_file_path(fn);
|
std::string dir = base::get_file_path(fn);
|
||||||
if (!base::is_directory(dir))
|
if (!base::is_directory(dir))
|
||||||
@ -125,30 +87,109 @@ void RecentFiles::removeRecentFile(const std::string& filename)
|
|||||||
void RecentFiles::removeRecentFolder(const std::string& dir)
|
void RecentFiles::removeRecentFolder(const std::string& dir)
|
||||||
{
|
{
|
||||||
std::string fn = normalizePath(dir);
|
std::string fn = normalizePath(dir);
|
||||||
m_paths.removeItem(fn, compare_path(fn));
|
removeItem(m_paths[kRecentFolders], fn);
|
||||||
|
|
||||||
Changed();
|
Changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecentFiles::setLimit(const int n)
|
void RecentFiles::setLimit(const int newLimit)
|
||||||
{
|
{
|
||||||
m_files.setLimit(n);
|
ASSERT(newLimit >= 0);
|
||||||
m_paths.setLimit(n);
|
|
||||||
|
|
||||||
|
for (auto& list : m_paths) {
|
||||||
|
if (newLimit < list.size()) {
|
||||||
|
auto it = list.begin();
|
||||||
|
std::advance(it, newLimit);
|
||||||
|
list.erase(it, list.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_limit = newLimit;
|
||||||
Changed();
|
Changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecentFiles::clear()
|
void RecentFiles::clear()
|
||||||
{
|
{
|
||||||
m_files.clear();
|
// Clear only recent items (not pinned items)
|
||||||
m_paths.clear();
|
m_paths[kRecentFiles].clear();
|
||||||
|
m_paths[kRecentFolders].clear();
|
||||||
|
|
||||||
Changed();
|
Changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RecentFiles::setFiles(const base::paths& pinnedFiles,
|
||||||
|
const base::paths& recentFiles)
|
||||||
|
{
|
||||||
|
m_paths[kPinnedFiles] = pinnedFiles;
|
||||||
|
m_paths[kRecentFiles] = recentFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentFiles::setFolders(const base::paths& pinnedFolders,
|
||||||
|
const base::paths& recentFolders)
|
||||||
|
{
|
||||||
|
m_paths[kPinnedFolders] = pinnedFolders;
|
||||||
|
m_paths[kRecentFolders] = recentFolders;
|
||||||
|
}
|
||||||
|
|
||||||
std::string RecentFiles::normalizePath(const std::string& filename)
|
std::string RecentFiles::normalizePath(const std::string& filename)
|
||||||
{
|
{
|
||||||
return base::normalize_path(filename);
|
return base::normalize_path(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RecentFiles::addItem(base::paths& list, const std::string& fn)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(list.begin(), list.end(), compare_path(fn));
|
||||||
|
|
||||||
|
// If the item already exist in the list...
|
||||||
|
if (it != list.end()) {
|
||||||
|
// Move it to the first position
|
||||||
|
list.erase(it);
|
||||||
|
list.insert(list.begin(), fn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_limit > 0)
|
||||||
|
list.insert(list.begin(), fn);
|
||||||
|
|
||||||
|
while (list.size() > m_limit)
|
||||||
|
list.erase(--list.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentFiles::removeItem(base::paths& list, const std::string& fn)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(list.begin(), list.end(), compare_path(fn));
|
||||||
|
if (it != list.end())
|
||||||
|
list.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentFiles::load()
|
||||||
|
{
|
||||||
|
for (int i=0; i<kCollections; ++i) {
|
||||||
|
for (const auto& key : enum_config_keys(kSectionName[i])) {
|
||||||
|
const char* fn = get_config_string(kSectionName[i], key.c_str(), nullptr);
|
||||||
|
if (fn && *fn &&
|
||||||
|
((i < 2 && base::is_file(fn)) ||
|
||||||
|
(i >= 2 && base::is_directory(fn)))) {
|
||||||
|
std::string normalFn = normalizePath(fn);
|
||||||
|
m_paths[i].push_back(normalFn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentFiles::save()
|
||||||
|
{
|
||||||
|
for (int i=0; i<kCollections; ++i) {
|
||||||
|
for (const auto& key : enum_config_keys(kSectionName[i]))
|
||||||
|
del_config_value(kSectionName[i],
|
||||||
|
key.c_str());
|
||||||
|
|
||||||
|
for (int j=0; j<m_paths[i].size(); ++j) {
|
||||||
|
set_config_string(kSectionName[i],
|
||||||
|
base::convert_to<std::string>(j).c_str(),
|
||||||
|
m_paths[i][j].c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2018 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -8,7 +9,7 @@
|
|||||||
#define APP_RECENT_FILES_H_INCLUDED
|
#define APP_RECENT_FILES_H_INCLUDED
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "base/recent_items.h"
|
#include "base/paths.h"
|
||||||
#include "obs/signal.h"
|
#include "obs/signal.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -16,18 +17,16 @@
|
|||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
class RecentFiles {
|
class RecentFiles {
|
||||||
|
enum { kPinnedFiles,
|
||||||
|
kRecentFiles,
|
||||||
|
kPinnedFolders,
|
||||||
|
kRecentFolders,
|
||||||
|
kCollections };
|
||||||
public:
|
public:
|
||||||
typedef base::RecentItems<std::string> List;
|
const base::paths& pinnedFiles() { return m_paths[kPinnedFiles]; }
|
||||||
typedef List::iterator iterator;
|
const base::paths& recentFiles() { return m_paths[kRecentFiles]; }
|
||||||
typedef List::const_iterator const_iterator;
|
const base::paths& pinnedFolders() { return m_paths[kPinnedFolders]; }
|
||||||
|
const base::paths& recentFolders() { return m_paths[kRecentFolders]; }
|
||||||
// Iterate through recent files.
|
|
||||||
const_iterator files_begin() { return m_files.begin(); }
|
|
||||||
const_iterator files_end() { return m_files.end(); }
|
|
||||||
|
|
||||||
// Iterate through recent paths.
|
|
||||||
const_iterator paths_begin() { return m_paths.begin(); }
|
|
||||||
const_iterator paths_end() { return m_paths.end(); }
|
|
||||||
|
|
||||||
RecentFiles(const int limit);
|
RecentFiles(const int limit);
|
||||||
~RecentFiles();
|
~RecentFiles();
|
||||||
@ -35,16 +34,25 @@ namespace app {
|
|||||||
void addRecentFile(const std::string& filename);
|
void addRecentFile(const std::string& filename);
|
||||||
void removeRecentFile(const std::string& filename);
|
void removeRecentFile(const std::string& filename);
|
||||||
void removeRecentFolder(const std::string& dir);
|
void removeRecentFolder(const std::string& dir);
|
||||||
void setLimit(const int n);
|
void setLimit(const int newLimit);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
void setFiles(const base::paths& pinnedFiles,
|
||||||
|
const base::paths& recentFiles);
|
||||||
|
void setFolders(const base::paths& pinnedFolders,
|
||||||
|
const base::paths& recentFolders);
|
||||||
|
|
||||||
obs::signal<void()> Changed;
|
obs::signal<void()> Changed;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string normalizePath(const std::string& filename);
|
std::string normalizePath(const std::string& filename);
|
||||||
|
void addItem(base::paths& list, const std::string& filename);
|
||||||
|
void removeItem(base::paths& list, const std::string& filename);
|
||||||
|
void load();
|
||||||
|
void save();
|
||||||
|
|
||||||
List m_files;
|
base::paths m_paths[kCollections];
|
||||||
List m_paths;
|
int m_limit;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
222
src/app/ui/draggable_widget.h
Normal file
222
src/app/ui/draggable_widget.h
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2018 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifndef APP_UI_DRAGGABLE_WIDGET_H_INCLUDED
|
||||||
|
#define APP_UI_DRAGGABLE_WIDGET_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "os/surface.h"
|
||||||
|
#include "os/system.h"
|
||||||
|
#include "ui/graphics.h"
|
||||||
|
#include "ui/message.h"
|
||||||
|
#include "ui/overlay.h"
|
||||||
|
#include "ui/overlay_manager.h"
|
||||||
|
#include "ui/paint_event.h"
|
||||||
|
#include "ui/system.h"
|
||||||
|
#include "ui/view.h"
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
|
||||||
|
template<typename Base>
|
||||||
|
class DraggableWidget : public Base {
|
||||||
|
public:
|
||||||
|
template<typename...Args>
|
||||||
|
DraggableWidget(Args...args) : Base(args...) { }
|
||||||
|
|
||||||
|
bool onProcessMessage(ui::Message* msg) override {
|
||||||
|
switch (msg->type()) {
|
||||||
|
|
||||||
|
case ui::kSetCursorMessage:
|
||||||
|
if (m_floatingOverlay) {
|
||||||
|
const ui::MouseMessage* mouseMsg = static_cast<ui::MouseMessage*>(msg);
|
||||||
|
const gfx::Point mousePos = mouseMsg->position();
|
||||||
|
if (onCanDropItemsOutside() &&
|
||||||
|
!getParentBounds().contains(mousePos)) {
|
||||||
|
ui::set_mouse_cursor(ui::kForbiddenCursor);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ui::set_mouse_cursor(ui::kMoveCursor);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ui::kMouseDownMessage: {
|
||||||
|
const bool wasCaptured = this->hasCapture();
|
||||||
|
const bool result = Base::onProcessMessage(msg);
|
||||||
|
|
||||||
|
if (!wasCaptured && this->hasCapture()) {
|
||||||
|
const ui::MouseMessage* mouseMsg = static_cast<ui::MouseMessage*>(msg);
|
||||||
|
const gfx::Point mousePos = mouseMsg->position();
|
||||||
|
m_dragMousePos = mousePos;
|
||||||
|
m_floatingOffset = mouseMsg->position() - this->bounds().origin();
|
||||||
|
m_createFloatingOverlay = true;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ui::kMouseMoveMessage: {
|
||||||
|
const ui::MouseMessage* mouseMsg = static_cast<ui::MouseMessage*>(msg);
|
||||||
|
const gfx::Point mousePos = mouseMsg->position();
|
||||||
|
|
||||||
|
if (this->hasCapture() && m_createFloatingOverlay) {
|
||||||
|
if (this->manager()->pick(mousePos) != this) {
|
||||||
|
m_createFloatingOverlay = false;
|
||||||
|
if (!m_floatingOverlay)
|
||||||
|
createFloatingOverlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_floatingOverlay) {
|
||||||
|
m_floatingOverlay->moveOverlay(mousePos - m_floatingOffset);
|
||||||
|
|
||||||
|
bool inside = true;
|
||||||
|
if (onCanDropItemsOutside()) {
|
||||||
|
inside = getParentBounds().contains(mousePos);
|
||||||
|
if (inside) {
|
||||||
|
if (this->hasFlags(ui::HIDDEN)) {
|
||||||
|
this->disableFlags(ui::HIDDEN);
|
||||||
|
layoutParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!this->hasFlags(ui::HIDDEN)) {
|
||||||
|
this->enableFlags(ui::HIDDEN);
|
||||||
|
layoutParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onReorderWidgets(mousePos, inside);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ui::kMouseUpMessage: {
|
||||||
|
const ui::MouseMessage* mouseMsg = static_cast<ui::MouseMessage*>(msg);
|
||||||
|
const gfx::Point mousePos = mouseMsg->position();
|
||||||
|
|
||||||
|
m_wasDragged = (this->hasCapture() && m_floatingOverlay);
|
||||||
|
const bool result = Base::onProcessMessage(msg);
|
||||||
|
|
||||||
|
if (!this->hasCapture()) {
|
||||||
|
if (m_floatingOverlay) {
|
||||||
|
destroyFloatingOverlay();
|
||||||
|
ASSERT(!m_createFloatingOverlay);
|
||||||
|
onFinalDrop(getParentBounds().contains(mousePos));
|
||||||
|
}
|
||||||
|
else if (m_createFloatingOverlay)
|
||||||
|
m_createFloatingOverlay = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_wasDragged = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return Base::onProcessMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wasDragged() const {
|
||||||
|
return m_wasDragged;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDragging() const {
|
||||||
|
return m_isDragging;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void createFloatingOverlay() {
|
||||||
|
ASSERT(!m_floatingOverlay);
|
||||||
|
|
||||||
|
m_isDragging = true;
|
||||||
|
|
||||||
|
gfx::Size sz = getFloatingOverlaySize();
|
||||||
|
sz.w = std::max(1, sz.w);
|
||||||
|
sz.h = std::max(1, sz.h);
|
||||||
|
os::Surface* surface = os::instance()->createRgbaSurface(sz.w, sz.h);
|
||||||
|
|
||||||
|
{
|
||||||
|
os::SurfaceLock lock(surface);
|
||||||
|
surface->fillRect(gfx::rgba(0, 0, 0, 0),
|
||||||
|
gfx::Rect(0, 0, surface->width(), surface->height()));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ui::Graphics g(surface, 0, 0);
|
||||||
|
g.setFont(this->font());
|
||||||
|
drawFloatingOverlay(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_floatingOverlay.reset(new ui::Overlay(surface, gfx::Point(),
|
||||||
|
ui::Overlay::MouseZOrder-1));
|
||||||
|
ui::OverlayManager::instance()->addOverlay(m_floatingOverlay.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyFloatingOverlay() {
|
||||||
|
ui::OverlayManager::instance()->removeOverlay(m_floatingOverlay.get());
|
||||||
|
m_floatingOverlay.reset();
|
||||||
|
m_isDragging = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gfx::Size getFloatingOverlaySize() {
|
||||||
|
auto view = ui::View::getView(this->parent());
|
||||||
|
if (view)
|
||||||
|
return (view->viewportBounds().offset(view->viewScroll()) & this->bounds()).size();
|
||||||
|
else
|
||||||
|
return this->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
gfx::Rect getParentBounds() {
|
||||||
|
auto view = ui::View::getView(this->parent());
|
||||||
|
if (view)
|
||||||
|
return view->viewportBounds();
|
||||||
|
else
|
||||||
|
return this->parent()->bounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
void layoutParent() {
|
||||||
|
this->parent()->layout();
|
||||||
|
auto view = ui::View::getView(this->parent());
|
||||||
|
if (view)
|
||||||
|
return view->updateView();
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawFloatingOverlay(ui::Graphics& g) {
|
||||||
|
ui::PaintEvent ev(this, &g);
|
||||||
|
this->onPaint(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool onCanDropItemsOutside() { return true; }
|
||||||
|
virtual void onReorderWidgets(const gfx::Point& mousePos, bool inside) { }
|
||||||
|
virtual void onFinalDrop(bool inside) { }
|
||||||
|
|
||||||
|
// True if we should create the floating overlay after leaving the
|
||||||
|
// widget bounds.
|
||||||
|
bool m_createFloatingOverlay = false;
|
||||||
|
|
||||||
|
bool m_isDragging = false;
|
||||||
|
|
||||||
|
// True when the mouse button is released (drop operation) and we've
|
||||||
|
// dragged the widget to other position. Can be used to avoid
|
||||||
|
// triggering the default click operation by derived classes when
|
||||||
|
// we've dragged the widget.
|
||||||
|
bool m_wasDragged = false;
|
||||||
|
|
||||||
|
// Initial mouse position when we start the dragging process.
|
||||||
|
gfx::Point m_dragMousePos;
|
||||||
|
|
||||||
|
// Overlay used to show the floating widget (this overlay floats
|
||||||
|
// next to the mouse cursor).
|
||||||
|
std::unique_ptr<ui::Overlay> m_floatingOverlay;
|
||||||
|
|
||||||
|
// Relative mouse position between the widget and the overlay.
|
||||||
|
gfx::Point m_floatingOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace app
|
||||||
|
|
||||||
|
#endif
|
@ -121,8 +121,6 @@ void EditorView::onSetViewScroll(const gfx::Point& pt)
|
|||||||
|
|
||||||
void EditorView::onScrollRegion(ui::ScrollRegionEvent& ev)
|
void EditorView::onScrollRegion(ui::ScrollRegionEvent& ev)
|
||||||
{
|
{
|
||||||
View::onScrollRegion(ev);
|
|
||||||
|
|
||||||
gfx::Region& region = ev.region();
|
gfx::Region& region = ev.region();
|
||||||
Editor* editor = this->editor();
|
Editor* editor = this->editor();
|
||||||
ASSERT(editor);
|
ASSERT(editor);
|
||||||
|
@ -17,8 +17,6 @@ namespace app {
|
|||||||
|
|
||||||
void FileListView::onScrollRegion(ui::ScrollRegionEvent& ev)
|
void FileListView::onScrollRegion(ui::ScrollRegionEvent& ev)
|
||||||
{
|
{
|
||||||
View::onScrollRegion(ev);
|
|
||||||
|
|
||||||
if (auto fileList = dynamic_cast<FileList*>(attachedWidget())) {
|
if (auto fileList = dynamic_cast<FileList*>(attachedWidget())) {
|
||||||
gfx::Rect tbounds = fileList->thumbnailBounds();
|
gfx::Rect tbounds = fileList->thumbnailBounds();
|
||||||
if (!tbounds.isEmpty()) {
|
if (!tbounds.isEmpty()) {
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "app/recent_files.h"
|
#include "app/recent_files.h"
|
||||||
#include "app/ui/file_list.h"
|
#include "app/ui/file_list.h"
|
||||||
#include "app/ui/file_list_view.h"
|
#include "app/ui/file_list_view.h"
|
||||||
|
#include "app/ui/separator_in_view.h"
|
||||||
#include "app/ui/skin/skin_theme.h"
|
#include "app/ui/skin/skin_theme.h"
|
||||||
#include "app/widget_loader.h"
|
#include "app/widget_loader.h"
|
||||||
#include "base/bind.h"
|
#include "base/bind.h"
|
||||||
@ -36,6 +37,7 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <list>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -675,14 +677,20 @@ void FileSelector::updateLocation()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add paths from recent files list
|
// Add paths from recent files list
|
||||||
{
|
auto recent = App::instance()->recentFiles();
|
||||||
location()->addItem("");
|
if (!recent->pinnedFolders().empty()) {
|
||||||
location()->addItem("-------- Recent Paths --------");
|
auto sep = new SeparatorInView(Strings::file_selector_pinned_folders(), HORIZONTAL);
|
||||||
|
sep->setMinSize(gfx::Size(0, sep->sizeHint().h*2));
|
||||||
auto it = App::instance()->recentFiles()->paths_begin();
|
location()->addItem(sep);
|
||||||
auto end = App::instance()->recentFiles()->paths_end();
|
for (const auto& fn : recent->pinnedFolders())
|
||||||
for (; it != end; ++it)
|
location()->addItem(new CustomFolderNameItem(fn.c_str()));
|
||||||
location()->addItem(new CustomFolderNameItem(it->c_str()));
|
}
|
||||||
|
if (!recent->recentFolders().empty()) {
|
||||||
|
auto sep = new SeparatorInView(Strings::file_selector_recent_folders(), HORIZONTAL);
|
||||||
|
sep->setMinSize(gfx::Size(0, sep->sizeHint().h*2));
|
||||||
|
location()->addItem(sep);
|
||||||
|
for (const auto& fn : recent->recentFolders())
|
||||||
|
location()->addItem(new CustomFolderNameItem(fn.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select the location
|
// Select the location
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2018 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -16,6 +17,7 @@
|
|||||||
#include "app/i18n/strings.h"
|
#include "app/i18n/strings.h"
|
||||||
#include "app/pref/preferences.h"
|
#include "app/pref/preferences.h"
|
||||||
#include "app/recent_files.h"
|
#include "app/recent_files.h"
|
||||||
|
#include "app/ui/draggable_widget.h"
|
||||||
#include "app/ui/skin/skin_theme.h"
|
#include "app/ui/skin/skin_theme.h"
|
||||||
#include "app/ui_context.h"
|
#include "app/ui_context.h"
|
||||||
#include "base/bind.h"
|
#include "base/bind.h"
|
||||||
@ -26,6 +28,7 @@
|
|||||||
#include "ui/listitem.h"
|
#include "ui/listitem.h"
|
||||||
#include "ui/message.h"
|
#include "ui/message.h"
|
||||||
#include "ui/paint_event.h"
|
#include "ui/paint_event.h"
|
||||||
|
#include "ui/scroll_region_event.h"
|
||||||
#include "ui/size_hint_event.h"
|
#include "ui/size_hint_event.h"
|
||||||
#include "ui/system.h"
|
#include "ui/system.h"
|
||||||
#include "ui/view.h"
|
#include "ui/view.h"
|
||||||
@ -38,16 +41,30 @@ using namespace skin;
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// RecentFileItem
|
// RecentFileItem
|
||||||
|
|
||||||
class RecentFileItem : public LinkLabel {
|
class RecentFileItem : public DraggableWidget<LinkLabel> {
|
||||||
public:
|
public:
|
||||||
RecentFileItem(const std::string& file)
|
RecentFileItem(const std::string& file,
|
||||||
: LinkLabel("")
|
const bool pinned)
|
||||||
|
: DraggableWidget<LinkLabel>("")
|
||||||
, m_fullpath(file)
|
, m_fullpath(file)
|
||||||
, m_name(base::get_file_name(file))
|
, m_name(base::get_file_name(file))
|
||||||
, m_path(base::get_file_path(file)) {
|
, m_path(base::get_file_path(file))
|
||||||
|
, m_pinned(pinned) {
|
||||||
initTheme();
|
initTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& fullpath() const { return m_fullpath; }
|
||||||
|
bool pinned() const { return m_pinned; }
|
||||||
|
|
||||||
|
void pin() {
|
||||||
|
m_pinned = true;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onScrollRegion(ui::ScrollRegionEvent& ev) {
|
||||||
|
ev.region() -= gfx::Region(pinBounds(bounds()));
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onInitTheme(InitThemeEvent& ev) override {
|
void onInitTheme(InitThemeEvent& ev) override {
|
||||||
LinkLabel::onInitTheme(ev);
|
LinkLabel::onInitTheme(ev);
|
||||||
@ -68,6 +85,53 @@ protected:
|
|||||||
ev.setSizeHint(gfx::Size(sz1.w+sz2.w, MAX(sz1.h, sz2.h)));
|
ev.setSizeHint(gfx::Size(sz1.w+sz2.w, MAX(sz1.h, sz2.h)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool onProcessMessage(Message* msg) override {
|
||||||
|
switch (msg->type()) {
|
||||||
|
case kMouseDownMessage: {
|
||||||
|
const gfx::Point mousePos = static_cast<MouseMessage*>(msg)->position();
|
||||||
|
gfx::Rect rc = pinBounds(bounds());
|
||||||
|
rc.y = bounds().y;
|
||||||
|
rc.h = bounds().h;
|
||||||
|
if (rc.contains(mousePos)) {
|
||||||
|
m_pinned = !m_pinned;
|
||||||
|
invalidate();
|
||||||
|
|
||||||
|
auto parent = this->parent();
|
||||||
|
const auto& children = parent->children();
|
||||||
|
auto end = children.end();
|
||||||
|
auto moveTo = parent->firstChild();
|
||||||
|
if (m_pinned) {
|
||||||
|
for (auto it=children.begin(); it != end; ++it) {
|
||||||
|
if (*it == this || !static_cast<RecentFileItem*>(*it)->pinned()) {
|
||||||
|
moveTo = *it;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto it = std::find(children.begin(), end, this);
|
||||||
|
if (it != end) {
|
||||||
|
auto prevIt = it++;
|
||||||
|
for (; it != end; prevIt=it++) {
|
||||||
|
if (!static_cast<RecentFileItem*>(*it)->pinned())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
moveTo = *prevIt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this != moveTo) {
|
||||||
|
parent->moveChildTo(this, moveTo);
|
||||||
|
parent->layout();
|
||||||
|
}
|
||||||
|
saveConfig();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DraggableWidget<LinkLabel>::onProcessMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
void onPaint(PaintEvent& ev) override {
|
void onPaint(PaintEvent& ev) override {
|
||||||
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
|
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
|
||||||
Graphics* g = ev.graphics();
|
Graphics* g = ev.graphics();
|
||||||
@ -86,16 +150,80 @@ protected:
|
|||||||
setTextQuiet(m_path.c_str());
|
setTextQuiet(m_path.c_str());
|
||||||
theme->paintWidget(g, this, styleDetail, detailsBounds);
|
theme->paintWidget(g, this, styleDetail, detailsBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isDragging() && (m_pinned || hasMouse())) {
|
||||||
|
ui::Style* pinStyle = theme->styles.recentFilePin();
|
||||||
|
const gfx::Rect pinBounds = this->pinBounds(bounds);
|
||||||
|
PaintWidgetPartInfo pi;
|
||||||
|
pi.styleFlags =
|
||||||
|
(isSelected() ? ui::Style::Layer::kSelected: 0) |
|
||||||
|
(m_pinned ? ui::Style::Layer::kFocus: 0) |
|
||||||
|
(hasMouse() ? ui::Style::Layer::kMouse: 0);
|
||||||
|
theme->paintWidgetPart(g, pinStyle, pinBounds, pi);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onClick() override {
|
void onClick() override {
|
||||||
static_cast<RecentListBox*>(parent())->onClick(m_fullpath);
|
if (!wasDragged())
|
||||||
|
static_cast<RecentListBox*>(parent())->onClick(m_fullpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onReorderWidgets(const gfx::Point& mousePos, bool inside) override {
|
||||||
|
auto parent = this->parent();
|
||||||
|
auto other = manager()->pick(mousePos);
|
||||||
|
if (other && other != this && other->parent() == parent) {
|
||||||
|
parent->moveChildTo(this, other);
|
||||||
|
parent->layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onFinalDrop(bool inside) override {
|
||||||
|
if (!wasDragged())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (inside) {
|
||||||
|
// Pin all elements to keep the order
|
||||||
|
const auto& children = parent()->children();
|
||||||
|
for (auto it=children.rbegin(), end=children.rend(); it!=end; ++it) {
|
||||||
|
if (this == *it) {
|
||||||
|
for (; it!=end; ++it)
|
||||||
|
static_cast<RecentFileItem*>(*it)->pin();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setVisible(false);
|
||||||
|
parent()->layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
saveConfig();
|
||||||
|
|
||||||
|
if (!inside)
|
||||||
|
deferDelete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
gfx::Rect pinBounds(const gfx::Rect& bounds) {
|
||||||
|
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
|
||||||
|
ui::Style* pinStyle = theme->styles.recentFilePin();
|
||||||
|
ui::View* view = View::getView(parent());
|
||||||
|
const gfx::Size pinSize = theme->calcSizeHint(this, pinStyle);
|
||||||
|
const gfx::Rect vp = view->viewportBounds();
|
||||||
|
const gfx::Point scroll = view->viewScroll();
|
||||||
|
return gfx::Rect(scroll.x+bounds.x+vp.w-pinSize.w,
|
||||||
|
bounds.y+bounds.h/2-pinSize.h/2,
|
||||||
|
pinSize.w, pinSize.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveConfig() {
|
||||||
|
static_cast<RecentListBox*>(parent())->updateRecentListFromUIItems();
|
||||||
|
}
|
||||||
|
|
||||||
std::string m_fullpath;
|
std::string m_fullpath;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::string m_path;
|
std::string m_path;
|
||||||
|
bool m_pinned;
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
@ -129,6 +257,29 @@ void RecentListBox::rebuildList()
|
|||||||
layout();
|
layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RecentListBox::updateRecentListFromUIItems()
|
||||||
|
{
|
||||||
|
base::paths pinnedPaths;
|
||||||
|
base::paths recentPaths;
|
||||||
|
for (auto item : children()) {
|
||||||
|
auto fi = static_cast<RecentFileItem*>(item);
|
||||||
|
if (fi->hasFlags(ui::HIDDEN))
|
||||||
|
continue;
|
||||||
|
if (fi->pinned())
|
||||||
|
pinnedPaths.push_back(fi->fullpath());
|
||||||
|
else
|
||||||
|
recentPaths.push_back(fi->fullpath());
|
||||||
|
}
|
||||||
|
onUpdateRecentListFromUIItems(pinnedPaths,
|
||||||
|
recentPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecentListBox::onScrollRegion(ui::ScrollRegionEvent& ev)
|
||||||
|
{
|
||||||
|
for (auto item : children())
|
||||||
|
static_cast<RecentFileItem*>(item)->onScrollRegion(ev);
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// RecentFilesListBox
|
// RecentFilesListBox
|
||||||
|
|
||||||
@ -140,10 +291,10 @@ RecentFilesListBox::RecentFilesListBox()
|
|||||||
void RecentFilesListBox::onRebuildList()
|
void RecentFilesListBox::onRebuildList()
|
||||||
{
|
{
|
||||||
auto recent = App::instance()->recentFiles();
|
auto recent = App::instance()->recentFiles();
|
||||||
auto it = recent->files_begin();
|
for (const auto& fn : recent->pinnedFiles())
|
||||||
auto end = recent->files_end();
|
addChild(new RecentFileItem(fn, true));
|
||||||
for (; it != end; ++it)
|
for (const auto& fn : recent->recentFiles())
|
||||||
addChild(new RecentFileItem(it->c_str()));
|
addChild(new RecentFileItem(fn, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecentFilesListBox::onClick(const std::string& path)
|
void RecentFilesListBox::onClick(const std::string& path)
|
||||||
@ -160,6 +311,13 @@ void RecentFilesListBox::onClick(const std::string& path)
|
|||||||
UIContext::instance()->executeCommand(command, params);
|
UIContext::instance()->executeCommand(command, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RecentFilesListBox::onUpdateRecentListFromUIItems(const base::paths& pinnedPaths,
|
||||||
|
const base::paths& recentPaths)
|
||||||
|
{
|
||||||
|
App::instance()->recentFiles()->setFiles(pinnedPaths,
|
||||||
|
recentPaths);
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// RecentFoldersListBox
|
// RecentFoldersListBox
|
||||||
|
|
||||||
@ -171,10 +329,10 @@ RecentFoldersListBox::RecentFoldersListBox()
|
|||||||
void RecentFoldersListBox::onRebuildList()
|
void RecentFoldersListBox::onRebuildList()
|
||||||
{
|
{
|
||||||
auto recent = App::instance()->recentFiles();
|
auto recent = App::instance()->recentFiles();
|
||||||
auto it = recent->paths_begin();
|
for (const auto& fn : recent->pinnedFolders())
|
||||||
auto end = recent->paths_end();
|
addChild(new RecentFileItem(fn, true));
|
||||||
for (; it != end; ++it)
|
for (const auto& fn : recent->recentFolders())
|
||||||
addChild(new RecentFileItem(*it));
|
addChild(new RecentFileItem(fn, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RecentFoldersListBox::onClick(const std::string& path)
|
void RecentFoldersListBox::onClick(const std::string& path)
|
||||||
@ -191,4 +349,11 @@ void RecentFoldersListBox::onClick(const std::string& path)
|
|||||||
UIContext::instance()->executeCommand(command, params);
|
UIContext::instance()->executeCommand(command, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RecentFoldersListBox::onUpdateRecentListFromUIItems(const base::paths& pinnedPaths,
|
||||||
|
const base::paths& recentPaths)
|
||||||
|
{
|
||||||
|
App::instance()->recentFiles()->setFolders(pinnedPaths,
|
||||||
|
recentPaths);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2018 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2016 David Capello
|
// Copyright (C) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -8,21 +9,31 @@
|
|||||||
#define APP_UI_RECENT_LISTBOX_H_INCLUDED
|
#define APP_UI_RECENT_LISTBOX_H_INCLUDED
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/paths.h"
|
||||||
#include "obs/connection.h"
|
#include "obs/connection.h"
|
||||||
#include "ui/listbox.h"
|
#include "ui/listbox.h"
|
||||||
|
#include "ui/view.h"
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
class RecentFileItem;
|
class RecentFileItem;
|
||||||
|
|
||||||
class RecentListBox : public ui::ListBox {
|
class RecentListBox : public ui::ListBox,
|
||||||
|
public ui::ViewableWidget {
|
||||||
friend class RecentFileItem;
|
friend class RecentFileItem;
|
||||||
public:
|
public:
|
||||||
RecentListBox();
|
RecentListBox();
|
||||||
|
|
||||||
|
void updateRecentListFromUIItems();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// ui::ViewableWidget impl
|
||||||
|
virtual void onScrollRegion(ui::ScrollRegionEvent& ev);
|
||||||
|
|
||||||
virtual void onRebuildList() = 0;
|
virtual void onRebuildList() = 0;
|
||||||
virtual void onClick(const std::string& path) = 0;
|
virtual void onClick(const std::string& path) = 0;
|
||||||
|
virtual void onUpdateRecentListFromUIItems(const base::paths& pinnedPaths,
|
||||||
|
const base::paths& recentPaths) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void rebuildList();
|
void rebuildList();
|
||||||
@ -35,18 +46,22 @@ namespace app {
|
|||||||
public:
|
public:
|
||||||
RecentFilesListBox();
|
RecentFilesListBox();
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
void onRebuildList() override;
|
void onRebuildList() override;
|
||||||
void onClick(const std::string& path) override;
|
void onClick(const std::string& path) override;
|
||||||
|
void onUpdateRecentListFromUIItems(const base::paths& pinnedPaths,
|
||||||
|
const base::paths& recentPaths) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RecentFoldersListBox : public RecentListBox {
|
class RecentFoldersListBox : public RecentListBox {
|
||||||
public:
|
public:
|
||||||
RecentFoldersListBox();
|
RecentFoldersListBox();
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
void onRebuildList() override;
|
void onRebuildList() override;
|
||||||
void onClick(const std::string& path) override;
|
void onClick(const std::string& path) override;
|
||||||
|
void onUpdateRecentListFromUIItems(const base::paths& pinnedPaths,
|
||||||
|
const base::paths& recentPaths) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
// #define DEBUG_PAINT_EVENTS
|
// #define DEBUG_PAINT_EVENTS
|
||||||
// #define LIMIT_DISPATCH_TIME
|
// #define LIMIT_DISPATCH_TIME
|
||||||
// #define DEBUG_UI_THREADS
|
// #define DEBUG_UI_THREADS
|
||||||
|
#define GARBAGE_TRACE(...)
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -351,6 +352,17 @@ void Manager::generateMessagesFromOSEvents()
|
|||||||
|
|
||||||
if (canWait && used_msg_queue.empty())
|
if (canWait && used_msg_queue.empty())
|
||||||
collectGarbage();
|
collectGarbage();
|
||||||
|
#if _DEBUG
|
||||||
|
else if (!m_garbage.empty()) {
|
||||||
|
GARBAGE_TRACE("collectGarbage() wasn't called #objects=%d"
|
||||||
|
" (msg_queue=%d used_msg_queue=%d redrawState=%d runningTimers=%d)\n",
|
||||||
|
int(m_garbage.size()),
|
||||||
|
msg_queue.size(),
|
||||||
|
used_msg_queue.size(),
|
||||||
|
int(redrawState),
|
||||||
|
Timer::haveRunningTimers());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
m_eventQueue->getEvent(sheEvent, canWait);
|
m_eventQueue->getEvent(sheEvent, canWait);
|
||||||
if (sheEvent.type() == os::Event::None)
|
if (sheEvent.type() == os::Event::None)
|
||||||
@ -1568,10 +1580,13 @@ void Manager::collectGarbage()
|
|||||||
if (m_garbage.empty())
|
if (m_garbage.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (WidgetsList::iterator
|
GARBAGE_TRACE("Manager::collectGarbage() #objects=%d\n", int(m_garbage.size()));
|
||||||
it = m_garbage.begin(),
|
|
||||||
end = m_garbage.end(); it != end; ++it) {
|
for (auto widget : m_garbage) {
|
||||||
delete *it;
|
GARBAGE_TRACE(" -> deleting %s %s ---\n",
|
||||||
|
typeid(*widget).name(),
|
||||||
|
widget->id().c_str());
|
||||||
|
delete widget;
|
||||||
}
|
}
|
||||||
m_garbage.clear();
|
m_garbage.clear();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite UI Library
|
// Aseprite UI Library
|
||||||
|
// Copyright (C) 2018 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -13,6 +14,7 @@
|
|||||||
#include "os/display.h"
|
#include "os/display.h"
|
||||||
#include "os/surface.h"
|
#include "os/surface.h"
|
||||||
#include "os/system.h"
|
#include "os/system.h"
|
||||||
|
#include "ui/overlay_manager.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -27,6 +29,11 @@ void move_region(Manager* manager, const Region& region, int dx, int dy)
|
|||||||
if (!display)
|
if (!display)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
auto overlays = ui::OverlayManager::instance();
|
||||||
|
gfx::Rect bounds = region.bounds();
|
||||||
|
bounds |= gfx::Rect(bounds).offset(dx, dy);
|
||||||
|
overlays->restoreOverlappedAreas(bounds);
|
||||||
|
|
||||||
os::Surface* surface = display->getSurface();
|
os::Surface* surface = display->getSurface();
|
||||||
os::SurfaceLock lock(surface);
|
os::SurfaceLock lock(surface);
|
||||||
std::size_t nrects = region.size();
|
std::size_t nrects = region.size();
|
||||||
@ -83,6 +90,8 @@ void move_region(Manager* manager, const Region& region, int dx, int dy)
|
|||||||
manager->dirtyRect(rc);
|
manager->dirtyRect(rc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
overlays->drawOverlays();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
@ -57,8 +57,10 @@ void Timer::start()
|
|||||||
assert_ui_thread();
|
assert_ui_thread();
|
||||||
|
|
||||||
m_lastTick = base::current_tick();
|
m_lastTick = base::current_tick();
|
||||||
m_running = true;
|
if (!m_running) {
|
||||||
++running_timers;
|
m_running = true;
|
||||||
|
++running_timers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer::stop()
|
void Timer::stop()
|
||||||
|
@ -356,7 +356,8 @@ void View::onSetViewScroll(const gfx::Point& pt)
|
|||||||
|
|
||||||
void View::onScrollRegion(ScrollRegionEvent& ev)
|
void View::onScrollRegion(ScrollRegionEvent& ev)
|
||||||
{
|
{
|
||||||
// Do nothing
|
if (auto viewable = dynamic_cast<ViewableWidget*>(attachedWidget()))
|
||||||
|
viewable->onScrollRegion(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void View::onScrollChange()
|
void View::onScrollChange()
|
||||||
|
@ -17,6 +17,12 @@
|
|||||||
namespace ui {
|
namespace ui {
|
||||||
class ScrollRegionEvent;
|
class ScrollRegionEvent;
|
||||||
|
|
||||||
|
class ViewableWidget {
|
||||||
|
public:
|
||||||
|
virtual ~ViewableWidget() { }
|
||||||
|
virtual void onScrollRegion(ScrollRegionEvent& ev) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class View : public Widget
|
class View : public Widget
|
||||||
, public ScrollableViewDelegate {
|
, public ScrollableViewDelegate {
|
||||||
public:
|
public:
|
||||||
|
@ -577,6 +577,19 @@ void Widget::insertChild(int index, Widget* child)
|
|||||||
child->m_parent = this;
|
child->m_parent = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Widget::moveChildTo(Widget* thisChild, Widget* toThisPosition)
|
||||||
|
{
|
||||||
|
auto itA = std::find(m_children.begin(), m_children.end(), thisChild);
|
||||||
|
auto itB = std::find(m_children.begin(), m_children.end(), toThisPosition);
|
||||||
|
if (itA == m_children.end()) {
|
||||||
|
ASSERT(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int index = itB - m_children.begin();
|
||||||
|
m_children.erase(itA);
|
||||||
|
m_children.insert(m_children.begin() + index, thisChild);
|
||||||
|
}
|
||||||
|
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
// LAYOUT & CONSTRAINT
|
// LAYOUT & CONSTRAINT
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
|
@ -212,6 +212,7 @@ namespace ui {
|
|||||||
void removeAllChildren();
|
void removeAllChildren();
|
||||||
void replaceChild(Widget* oldChild, Widget* newChild);
|
void replaceChild(Widget* oldChild, Widget* newChild);
|
||||||
void insertChild(int index, Widget* child);
|
void insertChild(int index, Widget* child);
|
||||||
|
void moveChildTo(Widget* thisChild, Widget* toThisPosition);
|
||||||
|
|
||||||
// ===============================================================
|
// ===============================================================
|
||||||
// LAYOUT & CONSTRAINT
|
// LAYOUT & CONSTRAINT
|
||||||
|
Loading…
x
Reference in New Issue
Block a user