Add mini-editor to see 1px preview when you zoom in.

+ Added EditorView widget.
This commit is contained in:
David Capello 2011-04-02 18:12:41 -03:00
parent d547a05f4d
commit 70e76380aa
10 changed files with 363 additions and 87 deletions

View File

@ -283,6 +283,7 @@ add_library(aseprite-library
widgets/editor/cursor.cpp
widgets/editor/editor.cpp
widgets/editor/editor_listeners.cpp
widgets/editor/editor_view.cpp
widgets/editor/keys.cpp
widgets/editor/pixels_movement.cpp
widgets/fileview.cpp

View File

@ -53,6 +53,7 @@
#include "util/render.h"
#include "widgets/color_bar.h"
#include "widgets/editor/editor.h"
#include "widgets/editor/editor_view.h"
#include "widgets/menuitem.h"
#include "widgets/statebar.h"
#include "widgets/tabs.h"
@ -184,7 +185,7 @@ int App::run()
colorbar = new ColorBar(box_colorbar->getAlign());
toolbar = toolbar_new();
tabsbar = new Tabs(m_tabsDelegate = new AppTabsDelegate());
view = editor_view_new();
view = new EditorView(EditorView::CurrentEditorMode);
editor = create_new_editor();
// configure all widgets to expansives

View File

@ -18,38 +18,109 @@
#include "config.h"
#include <vector>
#include <algorithm>
#include "gui/gui.h"
#include "document_wrappers.h"
#include "ui_context.h"
#include "app.h"
#include "modules/editors.h"
#include "app.h"
#include "document_wrappers.h"
#include "gui/gui.h"
#include "modules/gui.h"
#include "modules/palettes.h"
#include "raster/image.h"
#include "raster/sprite.h"
#include "ui_context.h"
#include "util/misc.h"
#include "widgets/editor/editor.h"
#include "widgets/editor/editor_view.h"
#include "widgets/popup_frame_pin.h"
#include "widgets/statebar.h"
#include <algorithm>
#include <vector>
#define FIXUP_TOP_WINDOW() \
app_get_top_window()->remap_window(); \
app_get_top_window()->invalidate();
typedef std::vector<Editor*> EditorList;
class EditorItem
{
public:
enum Type { Normal, Mini };
EditorItem(Editor* editor, Type type)
: m_editor(editor)
, m_type(type)
{ }
Editor* getEditor() const { return m_editor; }
Type getType() const { return m_type; }
private:
Editor* m_editor;
Type m_type;
};
typedef std::vector<EditorItem> EditorList;
Editor* current_editor = NULL;
Widget* box_editors = NULL;
static EditorList editors;
static Frame* mini_editor_frame = NULL;
static Editor* mini_editor = NULL;
static int is_document_in_some_editor(Document* document);
static Document* get_more_reliable_document();
static Widget* find_next_editor(Widget* widget);
static int count_parents(Widget* widget);
static void create_mini_editor_frame();
static void hide_mini_editor_frame();
static void update_mini_editor_frame(Editor* editor);
class WrappedEditor : public Editor,
public EditorListener
{
public:
WrappedEditor() {
addListener(this);
}
~WrappedEditor() {
removeListener(this);
}
// EditorListener implementation
void dispose() {
// Do nothing
}
void scrollChanged(Editor* editor) OVERRIDE {
// Show the mini editor
if (editor->getZoom() > 0) {
// If the mini frame does not exist, create it
if (!mini_editor_frame)
create_mini_editor_frame();
if (!mini_editor_frame->isVisible())
mini_editor_frame->open_window_bg();
update_mini_editor_frame(editor);
}
// Hide the mini editor
else {
hide_mini_editor_frame();
}
}
void documentChanged(Editor* editor) OVERRIDE {
if (editor == current_editor) {
update_mini_editor_frame(editor);
}
}
};
int init_module_editors()
{
return 0;
@ -57,13 +128,19 @@ int init_module_editors()
void exit_module_editors()
{
editors.clear();
if (mini_editor_frame) {
save_window_pos(mini_editor_frame, "MiniEditor");
delete mini_editor_frame;
mini_editor_frame = NULL;
}
ASSERT(editors.empty());
}
Editor* create_new_editor()
{
Editor* editor = new Editor();
editors.push_back(editor);
Editor* editor = new WrappedEditor();
editors.push_back(EditorItem(editor, EditorItem::Normal));
return editor;
}
@ -71,24 +148,29 @@ Editor* create_new_editor()
// It does not delete the editor.
void remove_editor(Editor* editor)
{
EditorList::iterator it = std::find(editors.begin(), editors.end(), editor);
for (EditorList::iterator
it = editors.begin(),
end = editors.end(); it != end; ++it) {
if (it->getEditor() == editor) {
editors.erase(it);
return;
}
}
ASSERT(it != editors.end());
editors.erase(it);
ASSERT(false && "Editor not found in the list");
}
void refresh_all_editors()
{
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
(*it)->invalidate();
it->getEditor()->invalidate();
}
}
void update_editors_with_document(const Document* document)
{
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
Editor* editor = *it;
Editor* editor = it->getEditor();
if (document == editor->getDocument())
editor->updateEditor();
@ -98,9 +180,9 @@ void update_editors_with_document(const Document* document)
void editors_draw_sprite(const Sprite* sprite, int x1, int y1, int x2, int y2)
{
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
Editor* editor = *it;
Editor* editor = it->getEditor();
if (sprite == editor->getSprite())
if (sprite == editor->getSprite() && editor->isVisible())
editor->drawSpriteSafe(x1, y1, x2, y2);
}
}
@ -178,7 +260,7 @@ void editors_hide_document(const Document* document)
bool refresh = (activeSprite == document->getSprite()) ? true: false;
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
Editor* editor = *it;
Editor* editor = it->getEditor();
if (document == editor->getDocument())
editor->setDocument(get_more_reliable_document());
@ -208,6 +290,8 @@ void set_current_editor(Editor* editor)
app_refresh_screen(document);
app_rebuild_documents_tabs();
update_mini_editor_frame(editor);
}
}
@ -237,7 +321,11 @@ void set_document_in_more_reliable_editor(Document* document)
// Search for any empty editor
if (best->getDocument()) {
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
Editor* editor = *it;
// Avoid using abnormal editors (mini, etc.)
if (it->getType() != EditorItem::Normal)
continue;
Editor* editor = it->getEditor();
if (!editor->getDocument()) {
best = editor;
@ -262,7 +350,7 @@ void split_editor(Editor* editor, int align)
// Create a new box to contain both editors, and a new view to put the new editor.
JWidget new_panel = jpanel_new(align);
View* new_view = editor_view_new();
View* new_view = new EditorView(EditorView::CurrentEditorMode);
Editor* new_editor = create_new_editor();
// Insert the "new_box" in the same location that the view.
@ -343,7 +431,7 @@ void close_editor(Editor* editor)
// Update all editors.
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
Editor* editor = *it;
Editor* editor = it->getEditor();
editor->updateEditor();
}
}
@ -385,7 +473,7 @@ void make_unique_editor(Editor* editor)
static int is_document_in_some_editor(Document* document)
{
for (EditorList::iterator it = editors.begin(); it != editors.end(); ++it) {
Editor* editor = *it;
Editor* editor = it->getEditor();
if (document == editor->getDocument())
return true;
@ -433,3 +521,65 @@ static int count_parents(Widget* widget)
count++;
return count;
}
static void create_mini_editor_frame()
{
// Create mini-editor
mini_editor_frame = new Frame(false, "Preview");
mini_editor_frame->child_spacing = 0;
mini_editor_frame->set_autoremap(false);
mini_editor_frame->set_wantfocus(false);
View* newView = new EditorView(EditorView::AlwaysSelected);
jwidget_expansive(newView, true);
mini_editor = new Editor();
editors.push_back(EditorItem(mini_editor, EditorItem::Mini));
newView->attachToView(mini_editor);
mini_editor_frame->addChild(newView);
// Default bounds
int width = JI_SCREEN_W/4;
int height = JI_SCREEN_H/4;
mini_editor_frame->setBounds
(gfx::Rect(JI_SCREEN_W - width - jrect_w(app_get_toolbar()->rc),
JI_SCREEN_H - height - jrect_h(app_get_statusbar()->rc),
width, height));
load_window_pos(mini_editor_frame, "MiniEditor");
}
static void hide_mini_editor_frame()
{
if (mini_editor_frame &&
mini_editor_frame->isVisible()) {
mini_editor_frame->closeWindow(NULL);
}
}
static void update_mini_editor_frame(Editor* editor)
{
if (!mini_editor)
return;
Document* document = editor->getDocument();
if (document && document->getSprite()) {
gfx::Rect visibleBounds = editor->getVisibleSpriteBounds();
gfx::Point pt = visibleBounds.getCenter();
// Set the same location as in the given editor.
if (mini_editor->getDocument() != document) {
mini_editor->setDocument(document);
mini_editor->setZoom(0);
}
mini_editor->centerInSpritePoint(pt.x, pt.y);
}
else {
// When the editor does not have a document, we hide the mini-editor.
hide_mini_editor_frame();
}
}

View File

@ -71,8 +71,6 @@
using namespace gfx;
using namespace tools;
static bool editor_view_msg_proc(JWidget widget, Message* msg);
static ToolLoopManager::Pointer pointer_from_msg(Message* msg)
{
ToolLoopManager::Pointer::Button button =
@ -83,22 +81,6 @@ static ToolLoopManager::Pointer pointer_from_msg(Message* msg)
return ToolLoopManager::Pointer(msg->mouse.x, msg->mouse.y, button);
}
View* editor_view_new()
{
View* widget = new View();
SkinTheme* theme = static_cast<SkinTheme*>(widget->getTheme());
int l = theme->get_part(PART_EDITOR_SELECTED_W)->w;
int t = theme->get_part(PART_EDITOR_SELECTED_N)->h;
int r = theme->get_part(PART_EDITOR_SELECTED_E)->w;
int b = theme->get_part(PART_EDITOR_SELECTED_S)->h;
jwidget_set_border(widget, l, t, r, b);
widget->hideScrollBars();
jwidget_add_hook(widget, JI_WIDGET, editor_view_msg_proc, NULL);
return widget;
}
Editor::Editor()
: Widget(editor_type())
{
@ -207,6 +189,9 @@ void Editor::setDocument(Document* document)
// Redraw the entire editor (because we have a new sprite to draw)
invalidate();
// Notify listeners
m_listeners.notifyDocumentChanged(this);
}
// Sets the scroll position of the editor
@ -246,17 +231,13 @@ void Editor::setEditorScroll(int x, int y, int use_refresh_region)
oldScroll.y - newScroll.y);
jregion_free(region);
/* m_widget->flags &= ~JI_DIRTY; */
}
/* else { */
/* if (m_refresh_region) { */
/* jregion_free (m_refresh_region); */
/* m_refresh_region = NULL; */
/* } */
/* } */
if (thick)
editor_draw_cursor(m_cursor_screen_x, m_cursor_screen_y);
// Notify listeners
m_listeners.notifyScrollChanged(this);
}
void Editor::updateEditor()
@ -725,6 +706,43 @@ void Editor::removeListener(EditorListener* listener)
m_listeners.removeListener(listener);
}
// Returns the visible area of the active sprite.
gfx::Rect Editor::getVisibleSpriteBounds()
{
// Return an empty rectangle if there is not a active sprite.
if (!m_sprite) return gfx::Rect();
View* view = View::getView(this);
Rect vp = view->getViewportBounds();
int x1, y1, x2, y2;
screenToEditor(vp.x, vp.y, &x1, &y1);
screenToEditor(vp.x+vp.w-1, vp.y+vp.h-1, &x2, &y2);
return Rect(x1, y1, x2-x1+1, y2-y1+1);
}
// Changes the scroll to see the given point as the center of the editor.
void Editor::centerInSpritePoint(int x, int y)
{
View* view = View::getView(this);
Rect vp = view->getViewportBounds();
hideDrawingCursor();
x = m_offset_x - (vp.w/2) + ((1<<m_zoom)>>1) + (x << m_zoom);
y = m_offset_y - (vp.h/2) + ((1<<m_zoom)>>1) + (y << m_zoom);
updateEditor();
setEditorScroll(x, y, false);
showDrawingCursor();
invalidate();
// Notify listeners
m_listeners.notifyScrollChanged(this);
}
void Editor::editor_update_statusbar_for_standby()
{
Tool* current_tool = getCurrentEditorTool();
@ -787,41 +805,6 @@ void Editor::editor_update_quicktool()
editor_update_statusbar_for_standby();
}
//////////////////////////////////////////////////////////////////////
// Message handler for the a view widget that contains an editor
static bool editor_view_msg_proc(JWidget widget, Message* msg)
{
switch (msg->type) {
case JM_SETPOS:
// This avoid the displacement of the widgets in the viewport
jrect_copy(widget->rc, &msg->setpos.rect);
static_cast<View*>(widget)->updateView();
return true;
case JM_DRAW:
{
Widget* viewport = static_cast<View*>(widget)->getViewport();
Widget* child = reinterpret_cast<JWidget>(jlist_first_data(viewport->children));
JRect pos = jwidget_get_rect(widget);
SkinTheme* theme = static_cast<SkinTheme*>(widget->getTheme());
theme->draw_bounds_nw(ji_screen,
pos->x1, pos->y1,
pos->x2-1, pos->y2-1,
(child == current_editor) ? PART_EDITOR_SELECTED_NW:
PART_EDITOR_NORMAL_NW, false);
jrect_free(pos);
}
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////
// Message handler for the editor

View File

@ -92,6 +92,12 @@ public:
void addListener(EditorListener* listener);
void removeListener(EditorListener* listener);
// Returns the visible area of the active sprite.
gfx::Rect getVisibleSpriteBounds();
// Changes the scroll to see the given point as the center of the editor.
void centerInSpritePoint(int x, int y);
void editor_update_statusbar_for_standby();
// in cursor.c
@ -213,7 +219,6 @@ private:
};
View* editor_view_new();
int editor_type();
#endif

View File

@ -27,6 +27,7 @@ public:
virtual ~EditorListener() { }
virtual void dispose() = 0;
virtual void scrollChanged(Editor* editor) = 0;
virtual void documentChanged(Editor* editor) = 0;
};
#endif // WIDGETS_EDITOR_LISTENER_H_INCLUDED

View File

@ -41,3 +41,8 @@ void EditorListeners::notifyScrollChanged(Editor* editor)
{
m_listeners.notify(&EditorListener::scrollChanged, editor);
}
void EditorListeners::notifyDocumentChanged(Editor* editor)
{
m_listeners.notify(&EditorListener::documentChanged, editor);
}

View File

@ -33,6 +33,7 @@ public:
void removeListener(EditorListener* listener);
void notifyScrollChanged(Editor* editor);
void notifyDocumentChanged(Editor* editor);
private:
Listeners<EditorListener> m_listeners;

View File

@ -0,0 +1,91 @@
/* ASE - Allegro Sprite Editor
* Copyright (C) 2001-2011 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "widgets/editor/editor_view.h"
#include "gui/list.h"
#include "gui/message.h"
#include "modules/editors.h"
#include "skin/skin_theme.h"
#include "widgets/editor/editor.h"
#include <allegro.h>
EditorView::EditorView(EditorView::Type type)
: View()
, m_type(type)
{
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
int l = theme->get_part(PART_EDITOR_SELECTED_W)->w;
int t = theme->get_part(PART_EDITOR_SELECTED_N)->h;
int r = theme->get_part(PART_EDITOR_SELECTED_E)->w;
int b = theme->get_part(PART_EDITOR_SELECTED_S)->h;
jwidget_set_border(this, l, t, r, b);
hideScrollBars();
}
bool EditorView::onProcessMessage(Message* msg)
{
switch (msg->type) {
case JM_SETPOS:
// This avoid the displacement of the widgets in the viewport
jrect_copy(this->rc, &msg->setpos.rect);
updateView();
return true;
case JM_DRAW:
{
Widget* viewport = getViewport();
Widget* child = reinterpret_cast<JWidget>(jlist_first_data(viewport->children));
JRect pos = jwidget_get_rect(this);
SkinTheme* theme = static_cast<SkinTheme*>(getTheme());
bool selected = false;
switch (m_type) {
// Only show the view selected if it is the current editor
case CurrentEditorMode:
selected = (child == current_editor);
break;
// Always show selected
case AlwaysSelected:
selected = true;
break;
}
theme->draw_bounds_nw(ji_screen,
pos->x1, pos->y1,
pos->x2-1, pos->y2-1,
selected ? PART_EDITOR_SELECTED_NW:
PART_EDITOR_NORMAL_NW, false);
jrect_free(pos);
}
return true;
}
return View::onProcessMessage(msg);
}

View File

@ -0,0 +1,38 @@
/* ASE - Allegro Sprite Editor
* Copyright (C) 2001-2011 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef WIDGETS_EDITOR_VIEW_H_INCLUDED
#define WIDGETS_EDITOR_VIEW_H_INCLUDED
#include "gui/view.h"
class EditorView : public View
{
public:
enum Type { CurrentEditorMode, AlwaysSelected };
EditorView(Type type);
protected:
bool onProcessMessage(Message* msg) OVERRIDE;
private:
Type m_type;
};
#endif // WIDGETS_EDITOR_VIEW_H_INCLUDED