Merge branch 'new-scroll'

This commit is contained in:
David Capello 2016-01-05 20:49:36 -03:00
commit e985227d95
25 changed files with 409 additions and 137 deletions

View File

@ -1,5 +1,5 @@
<!-- ASEPRITE -->
<!-- Copyright (C) 2001-2013, 2015 by David Capello -->
<!-- Copyright (C) 2001-2016 by David Capello -->
<gui>
<window id="file_selector" text="">
<vbox id="main">
@ -12,7 +12,7 @@
<button text="" id="new_folder_button" tooltip="New folder" />
<combobox id="location" expansive="true" />
</box>
<view id="file_view" expansive="true" />
<vbox id="file_view_placeholder" expansive="true" />
<grid columns="2">
<label text="File name:" />
<box id="file_name_placeholder" cell_align="horizontal" />

View File

@ -1,5 +1,5 @@
# Aseprite
# Copyright (C) 2001-2015 David Capello
# Copyright (C) 2001-2016 David Capello
# Generate a ui::Widget for each widget in a XML file
file(GLOB widget_files ${CMAKE_SOURCE_DIR}/data/widgets/*.xml)
@ -366,6 +366,7 @@ add_library(app-lib
ui/editor/transform_handles.cpp
ui/editor/zooming_state.cpp
ui/file_list.cpp
ui/file_list_view.cpp
ui/file_selector.cpp
ui/font_popup.cpp
ui/frame_tag_window.cpp

View File

@ -129,7 +129,7 @@ void ScrollCommand::onExecute(Context* context)
case Down: delta.y = +m_quantity * pixels; break;
}
current_editor->setEditorScroll(scroll+delta, true);
current_editor->setEditorScroll(scroll+delta);
}
std::string ScrollCommand::onGetFriendlyName() const

View File

@ -169,7 +169,7 @@ bool DrawingState::onMouseMove(Editor* editor, MouseMessage* msg)
HideBrushPreview hide(editor->brushPreview());
// Infinite scroll
gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir, true);
gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir);
tools::ToolLoopManager::Pointer pointer(editor->screenToEditor(mousePos),
button_from_msg(msg));

View File

@ -288,6 +288,15 @@ void Editor::backToPreviousState()
setStateInternal(EditorStatePtr(NULL));
}
void Editor::getInvalidDecoratoredRegion(gfx::Region& region)
{
// Remove decorated region that cannot be just moved because it
// must be redrawn in another position when the Editor's scroll
// changes (e.g. symmetry handles).
if ((m_flags & kShowDecorators) && m_decorator)
m_decorator->getInvalidDecoratoredRegion(this, region);
}
void Editor::setLayer(const Layer* layer)
{
bool changed = (m_layer != layer);
@ -354,39 +363,13 @@ void Editor::setDefaultScroll()
setEditorScroll(
gfx::Point(
m_padding.x - vp.w/2 + m_zoom.apply(m_sprite->width())/2,
m_padding.y - vp.h/2 + m_zoom.apply(m_sprite->height())/2), false);
m_padding.y - vp.h/2 + m_zoom.apply(m_sprite->height())/2));
}
// Sets the scroll position of the editor
void Editor::setEditorScroll(const gfx::Point& scroll, bool blitValidRegion)
void Editor::setEditorScroll(const gfx::Point& scroll)
{
HideBrushPreview hide(m_brushPreview);
View* view = View::getView(this);
Point oldScroll;
Region region;
Region invalidRegion;
if (blitValidRegion) {
getDrawableRegion(region, kCutTopWindows);
oldScroll = view->viewScroll();
// Remove decorated region that cannot be just moved because it
// must be redrawn in another position when the Editor's scroll
// changes (e.g. symmetry handles).
if ((m_flags & kShowDecorators) && m_decorator) {
m_decorator->getInvalidDecoratoredRegion(this, invalidRegion);
if (!invalidRegion.isEmpty())
region.createSubtraction(region, invalidRegion);
}
}
view->setViewScroll(scroll);
Point newScroll = view->viewScroll();
if (blitValidRegion) {
// Move screen with blits
scrollRegion(region, oldScroll - newScroll);
}
View::getView(this)->setViewScroll(scroll);
}
void Editor::setEditorZoom(const render::Zoom& zoom)
@ -862,7 +845,7 @@ void Editor::flashCurrentLayer()
}
}
gfx::Point Editor::autoScroll(MouseMessage* msg, AutoScroll dir, bool blitValidRegion)
gfx::Point Editor::autoScroll(MouseMessage* msg, AutoScroll dir)
{
// // Hide the brush preview
// HideBrushPreview hide(editor->brushPreview());
@ -891,7 +874,7 @@ gfx::Point Editor::autoScroll(MouseMessage* msg, AutoScroll dir, bool blitValidR
else {
scroll -= deltaScroll;
}
setEditorScroll(scroll, blitValidRegion);
setEditorScroll(scroll);
#if defined(_WIN32) || defined(__APPLE__)
mousePos -= delta;
@ -1101,7 +1084,7 @@ void Editor::centerInSpritePoint(const gfx::Point& spritePos)
m_padding.y - (vp.h/2) + m_zoom.apply(1)/2 + m_zoom.apply(spritePos.y));
updateEditor();
setEditorScroll(scroll, false);
setEditorScroll(scroll);
invalidate();
}
@ -1498,12 +1481,10 @@ void Editor::setZoomAndCenterInMouse(const Zoom& zoom,
padding.y - (screenPos.y-vp.y) + zoom.apply(spritePos.y+zoom.remove(1)/2) + int(zoom.apply(subpixelPos.y)));
if ((m_zoom != zoom) || (screenPos != view->viewScroll())) {
bool blitValidRegion = (m_zoom == zoom);
m_zoom = zoom;
updateEditor();
setEditorScroll(scrollPos, blitValidRegion);
setEditorScroll(scrollPos);
}
flushRedraw();

View File

@ -109,6 +109,7 @@ namespace app {
// the Editor, so it must be deleted by the caller.
EditorDecorator* decorator() { return m_decorator; }
void setDecorator(EditorDecorator* decorator) { m_decorator = decorator; }
void getInvalidDecoratoredRegion(gfx::Region& region);
EditorFlags editorFlags() const { return m_flags; }
void setEditorFlags(EditorFlags flags) { m_flags = flags; }
@ -129,7 +130,7 @@ namespace app {
void setZoom(const render::Zoom& zoom) { m_zoom = zoom; }
void setDefaultScroll();
void setEditorScroll(const gfx::Point& scroll, bool blitValidRegion);
void setEditorScroll(const gfx::Point& scroll);
void setEditorZoom(const render::Zoom& zoom);
// Updates the Editor's view.
@ -164,7 +165,7 @@ namespace app {
void updateStatusBar();
// Control scroll when cursor goes out of the editor viewport.
gfx::Point autoScroll(ui::MouseMessage* msg, AutoScroll dir, bool blitValidRegion);
gfx::Point autoScroll(ui::MouseMessage* msg, AutoScroll dir);
tools::Tool* getCurrentEditorTool();
tools::Ink* getCurrentEditorInk();

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
@ -21,6 +21,7 @@
#include "she/surface.h"
#include "ui/paint_event.h"
#include "ui/resize_event.h"
#include "ui/scroll_region_event.h"
namespace app {
@ -106,7 +107,7 @@ void EditorView::onResize(ResizeEvent& ev)
// This keeps the same scroll position for the editor
gfx::Point newPos = editor->editorToScreen(gfx::Point(0, 0));
gfx::Point oldScroll = viewScroll();
editor->setEditorScroll(oldScroll + newPos - oldPos, false);
editor->setEditorScroll(oldScroll + newPos - oldPos);
break;
}
case KeepCenter:
@ -116,6 +117,32 @@ void EditorView::onResize(ResizeEvent& ev)
}
}
void EditorView::onSetViewScroll(const gfx::Point& pt)
{
Editor* editor = this->editor();
if (editor) {
// We have to hide the brush preview to scroll (without this,
// keyboard shortcuts to scroll when the brush preview is visible
// will leave brush previews all over the screen).
HideBrushPreview hide(editor->brushPreview());
View::onSetViewScroll(pt);
}
}
void EditorView::onScrollRegion(ui::ScrollRegionEvent& ev)
{
View::onScrollRegion(ev);
gfx::Region& region = ev.region();
Editor* editor = this->editor();
ASSERT(editor);
if (editor) {
gfx::Region invalidRegion;
editor->getInvalidDecoratoredRegion(invalidRegion);
region.createSubtraction(region, invalidRegion);
}
}
void EditorView::onScrollChange()
{
View::onScrollChange();

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
@ -27,6 +27,8 @@ namespace app {
protected:
void onPaint(ui::PaintEvent& ev) override;
void onResize(ui::ResizeEvent& ev) override;
void onSetViewScroll(const gfx::Point& pt) override;
void onScrollRegion(ui::ScrollRegionEvent& ev) override;
void onScrollChange() override;
private:

View File

@ -309,7 +309,7 @@ bool MovingPixelsState::onMouseMove(Editor* editor, MouseMessage* msg)
// If there is a button pressed
if (m_pixelsMovement->isDragging()) {
// Auto-scroll
gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir, false);
gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir);
// Get the position of the mouse in the sprite
gfx::Point spritePos = editor->screenToEditor(mousePos);

View File

@ -54,7 +54,7 @@ bool ScrollingState::onMouseMove(Editor* editor, MouseMessage* msg)
gfx::Point newPos = msg->position();
#ifdef _WIN32
if (newPos != editor->autoScroll(msg, AutoScroll::ScrollDir, true)) {
if (newPos != editor->autoScroll(msg, AutoScroll::ScrollDir)) {
m_oldPos = newPos;
return true;
}
@ -63,7 +63,7 @@ bool ScrollingState::onMouseMove(Editor* editor, MouseMessage* msg)
scroll -= newPos - m_oldPos;
m_oldPos = newPos;
editor->setEditorScroll(scroll, true);
editor->setEditorScroll(scroll);
return true;
}

View File

@ -153,7 +153,7 @@ bool StateWithWheelBehavior::onMouseWheel(Editor* editor, MouseMessage* msg)
}
}
editor->setEditorScroll(scroll+delta, true);
editor->setEditorScroll(scroll+delta);
break;
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
@ -35,6 +35,7 @@ FileList::FileList()
: Widget(kGenericWidget)
, m_generateThumbnailTimer(200, this)
, m_monitoringTimer(50, this)
, m_thumbnail(nullptr)
{
setFocusStop(true);
setDoubleBuffered(true);
@ -302,6 +303,22 @@ bool FileList::onProcessMessage(Message* msg)
return Widget::onProcessMessage(msg);
}
int FileList::thumbnailY()
{
int y = 0;
for (IFileItem* fi : m_list) {
gfx::Size itemSize = getFileItemSize(fi);
if (fi == m_selected) {
if (fi->getThumbnail())
return y + itemSize.h/2;
else
break;
}
y += itemSize.h;
}
return 0;
}
void FileList::onPaint(ui::PaintEvent& ev)
{
Graphics* g = ev.graphics();
@ -313,15 +330,12 @@ void FileList::onPaint(ui::PaintEvent& ev)
int evenRow = 0;
gfx::Color bgcolor;
gfx::Color fgcolor;
she::Surface* thumbnail = NULL;
int thumbnail_y = 0;
g->fillRect(theme->colors.background(), bounds);
// rows
for (FileItemList::iterator
it=m_list.begin(), end=m_list.end(); it!=end; ++it) {
IFileItem* fi = *it;
m_thumbnail = nullptr;
for (IFileItem* fi : m_list) {
gfx::Size itemSize = getFileItemSize(fi);
if (fi == m_selected) {
@ -373,30 +387,38 @@ void FileList::onPaint(ui::PaintEvent& ev)
}
// Thumbnail position
if (fi == m_selected) {
thumbnail = fi->getThumbnail();
if (thumbnail)
thumbnail_y = y + itemSize.h/2;
}
if (fi == m_selected)
m_thumbnail = fi->getThumbnail();
y += itemSize.h;
evenRow ^= 1;
}
// Draw the thumbnail
if (thumbnail) {
x = vp.x+vp.w - 2*guiscale() - thumbnail->width();
y = thumbnail_y - thumbnail->height()/2 + this->bounds().y;
y = MID(vp.y+2*guiscale(), y, vp.y+vp.h-3*guiscale()-thumbnail->height());
x -= this->bounds().x;
y -= this->bounds().y;
g->blit(thumbnail, 0, 0, x, y, thumbnail->width(), thumbnail->height());
g->drawRect(gfx::rgba(0, 0, 0),
gfx::Rect(x-1, y-1, thumbnail->width()+1, thumbnail->height()+1));
if (m_thumbnail) {
gfx::Rect tbounds = thumbnailBounds();
g->blit(m_thumbnail, 0, 0, tbounds.x, tbounds.y, tbounds.w, tbounds.h);
g->drawRect(gfx::rgba(0, 0, 0), tbounds.enlarge(1));
}
}
gfx::Rect FileList::thumbnailBounds()
{
if (!m_selected ||
!m_selected->getThumbnail())
return gfx::Rect();
she::Surface* thumbnail = m_selected->getThumbnail();
View* view = View::getView(this);
gfx::Rect vp = view->viewportBounds();
int x = vp.x+vp.w - 2*guiscale() - thumbnail->width();
int y = thumbnailY() - thumbnail->height()/2 + bounds().y;
y = MID(vp.y+2*guiscale(), y, vp.y+vp.h-3*guiscale()-thumbnail->height());
x -= bounds().x;
y -= bounds().y;
return gfx::Rect(x, y, thumbnail->width(), thumbnail->height());
}
void FileList::onSizeHint(SizeHintEvent& ev)
{
if (!m_req_valid) {

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
@ -16,6 +16,10 @@
#include <string>
namespace she {
class Surface;
}
namespace app {
class FileList : public ui::Widget {
@ -34,6 +38,8 @@ namespace app {
void goUp();
gfx::Rect thumbnailBounds();
base::Signal0<void> FileSelected;
base::Signal0<void> FileAccepted;
base::Signal0<void> CurrentFolderChanged;
@ -55,6 +61,7 @@ namespace app {
int getSelectedIndex();
void selectIndex(int index);
void generatePreviewOfSelectedItem();
int thumbnailY();
IFileItem* m_currentFolder;
FileItemList m_list;
@ -78,6 +85,7 @@ namespace app {
// thumbnail to generate when the m_generateThumbnailTimer ticks.
IFileItem* m_itemToGenerateThumbnail;
she::Surface* m_thumbnail;
};
} // namespace app

View File

@ -0,0 +1,35 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/ui/file_list_view.h"
#include "app/ui/file_list.h"
#include "ui/scroll_region_event.h"
namespace app {
void FileListView::onScrollRegion(ui::ScrollRegionEvent& ev)
{
View::onScrollRegion(ev);
if (auto fileList = dynamic_cast<FileList*>(attachedWidget())) {
gfx::Rect tbounds = fileList->thumbnailBounds();
if (!tbounds.isEmpty()) {
tbounds
.enlarge(1)
.offset(fileList->bounds().origin());
ev.region().createSubtraction(ev.region(), gfx::Region(tbounds));
}
}
}
} // namespace app

View File

@ -0,0 +1,26 @@
// Aseprite
// Copyright (C) 2016 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
#ifndef APP_UI_FILE_LIST_VIEW_H_INCLUDED
#define APP_UI_FILE_LIST_VIEW_H_INCLUDED
#pragma once
#include "ui/view.h"
namespace app {
class FileListView : public ui::View {
public:
FileListView() { }
private:
void onScrollRegion(ui::ScrollRegionEvent& ev);
};
} // namespace app
#endif

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
@ -19,6 +19,7 @@
#include "app/modules/gui.h"
#include "app/recent_files.h"
#include "app/ui/file_list.h"
#include "app/ui/file_list_view.h"
#include "app/ui/skin/button_icon_impl.h"
#include "app/ui/skin/skin_theme.h"
#include "app/widget_loader.h"
@ -280,9 +281,13 @@ FileSelector::FileSelector(FileSelectorType type, FileSelectorDelegate* delegate
m_fileList = new FileList();
m_fileList->setId("fileview");
fileView()->attachToView(m_fileList);
m_fileName->setAssociatedFileList(m_fileList);
m_fileView = new FileListView();
m_fileView->attachToView(m_fileList);
m_fileView->setExpansive(true);
fileViewPlaceholder()->addChild(m_fileView);
goBackButton()->Click.connect(base::Bind<void>(&FileSelector::onGoBack, this));
goForwardButton()->Click.connect(base::Bind<void>(&FileSelector::onGoForward, this));
goUpButton()->Click.connect(base::Bind<void>(&FileSelector::onGoUp, this));

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
@ -26,6 +26,7 @@ namespace ui {
namespace app {
class CustomFileNameEntry;
class FileList;
class FileListView;
class IFileItem;
class FileSelector : public app::gen::FileSelector {
@ -62,6 +63,7 @@ namespace app {
std::string m_defExtension;
CustomFileNameEntry* m_fileName;
FileList* m_fileList;
FileListView* m_fileView;
// If true the navigation_history isn't
// modified if the current folder changes

View File

@ -1,5 +1,5 @@
// Aseprite Gfx Library
// Copyright (C) 2001-2013, 2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -99,6 +99,11 @@ namespace gfx {
Rect operator[](int i);
const Rect operator[](int i) const;
Region& operator+=(const Region& b) { return createUnion(*this, b); }
Region& operator|=(const Region& b) { return createUnion(*this, b); }
Region& operator&=(const Region& b) { return createIntersection(*this, b); }
Region& operator-=(const Region& b) { return createSubtraction(*this, b); }
private:
mutable details::Region m_region;
};

View File

@ -1058,6 +1058,9 @@ void Manager::onResize(ResizeEvent& ev)
gfx::Rect new_pos = ev.bounds();
setBoundsQuietly(new_pos);
// The whole manager area is invalid now.
m_invalidRegion = gfx::Region(new_pos);
int dx = new_pos.x - old_pos.x;
int dy = new_pos.y - old_pos.y;
int dw = new_pos.w - old_pos.w;
@ -1266,6 +1269,10 @@ void Manager::pumpQueue()
// Restore clip region for paint messages.
surface->setClipBounds(oldClip);
}
// As this kPaintMessage's rectangle was updated, we can
// remove it from "m_invalidRegion".
m_invalidRegion -= gfx::Region(paintMsg->rect());
}
}
else {

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2013, 2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -86,6 +86,19 @@ namespace ui {
bool isFocusMovementKey(Message* msg);
// Returns the invalid region in the screen to being updated with
// PaintMessages. This region is cleared when each widget receives
// a paint message.
const gfx::Region& getInvalidRegion() const {
return m_invalidRegion;
}
void addInvalidRegion(const gfx::Region& b) {
m_invalidRegion |= b;
}
// Mark the given rectangle as a area to be flipped to the real
// screen
void dirtyRect(const gfx::Rect& bounds);
void _openWindow(Window* window);
@ -130,6 +143,7 @@ namespace ui {
she::Display* m_display;
she::Clipboard* m_clipboard;
she::EventQueue* m_eventQueue;
gfx::Region m_invalidRegion; // Invalid region (we didn't receive paint messages yet for this).
// This member is used to make freeWidget() a no-op when we
// restack a window if the user clicks on it.

View File

@ -0,0 +1,30 @@
// Aseprite UI Library
// Copyright (C) 2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef UI_SCROLL_REGION_EVENT_H_INCLUDED
#define UI_SCROLL_REGION_EVENT_H_INCLUDED
#pragma once
#include "gfx/region.h"
#include "ui/event.h"
namespace ui {
class ScrollRegionEvent : public Event {
public:
ScrollRegionEvent(Component* source, gfx::Region& region)
: Event(source), m_region(region) {
}
gfx::Region& region() { return m_region; }
private:
gfx::Region& m_region;
};
} // namespace ui
#endif

View File

@ -1,24 +1,37 @@
// Aseprite UI Library
// Copyright (C) 2001-2013, 2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
// #define DEBUG_SCROLL_EVENTS
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gfx/size.h"
#include "ui/intern.h"
#include "ui/manager.h"
#include "ui/message.h"
#include "ui/size_hint_event.h"
#include "ui/move_region.h"
#include "ui/resize_event.h"
#include "ui/scroll_helper.h"
#include "ui/scroll_region_event.h"
#include "ui/size_hint_event.h"
#include "ui/system.h"
#include "ui/theme.h"
#include "ui/view.h"
#include "ui/widget.h"
#ifdef DEBUG_SCROLL_EVENTS
#include "base/thread.h"
#include "she/display.h"
#include "she/scoped_surface_lock.h"
#endif
#include <queue>
#define HBAR_SIZE (m_scrollbar_h.getBarWidth())
#define VBAR_SIZE (m_scrollbar_v.getBarWidth())
@ -33,7 +46,7 @@ View::View()
{
m_hasBars = true;
this->setFocusStop(true);
setFocusStop(true);
addChild(&m_viewport);
setScrollableSize(Size(0, 0));
@ -128,21 +141,7 @@ Point View::viewScroll() const
void View::setViewScroll(const Point& pt)
{
Point oldScroll = viewScroll();
Size maxsize = getScrollableSize();
Size visible = visibleSize();
Point newScroll(MID(0, pt.x, MAX(0, maxsize.w - visible.w)),
MID(0, pt.y, MAX(0, maxsize.h - visible.h)));
if (newScroll == oldScroll)
return;
m_scrollbar_h.setPos(newScroll.x);
m_scrollbar_v.setPos(newScroll.y);
m_viewport.layout();
onScrollChange();
onSetViewScroll(pt);
}
void View::updateView()
@ -228,6 +227,137 @@ void View::onPaint(PaintEvent& ev)
theme()->paintView(ev);
}
void View::onSetViewScroll(const gfx::Point& pt)
{
Point oldScroll = viewScroll();
Size maxsize = getScrollableSize();
Size visible = visibleSize();
Point newScroll(MID(0, pt.x, MAX(0, maxsize.w - visible.w)),
MID(0, pt.y, MAX(0, maxsize.h - visible.h)));
if (newScroll == oldScroll)
return;
// This is the movement for the scrolled region (which is inverse to
// the scroll position delta/movement).
Point delta = oldScroll - newScroll;
// Visible viewport region that is not overlapped by windows
Region drawableRegion;
m_viewport.getDrawableRegion(
drawableRegion, DrawableRegionFlags(kCutTopWindows | kUseChildArea));
// Start the region to scroll equal to the drawable viewport region.
Rect cpos = m_viewport.childrenBounds();
Region validRegion(cpos);
validRegion &= drawableRegion;
// Remove all children invalid regions from this "validRegion"
{
std::queue<Widget*> items;
items.push(&m_viewport);
while (!items.empty()) {
Widget* item = items.front();
items.pop();
for (Widget* child : item->children())
items.push(child);
if (item->isVisible())
validRegion -= item->getUpdateRegion();
}
}
// Remove invalid region in the screen (areas that weren't
// re-painted yet)
Manager* manager = this->manager();
if (manager)
validRegion -= manager->getInvalidRegion();
// Add extra regions that cannot be scrolled (this can be customized
// by subclassing ui::View). We use two ScrollRegionEvent, this
// first one with the old scroll position. And the next one with the
// new scroll position.
{
ScrollRegionEvent ev(this, validRegion);
onScrollRegion(ev);
}
// Move viewport children
cpos.offset(-newScroll);
for (auto child : m_viewport.children()) {
Size reqSize = child->sizeHint();
cpos.w = MAX(reqSize.w, cpos.w);
cpos.h = MAX(reqSize.h, cpos.h);
if (cpos.w != child->bounds().w ||
cpos.h != child->bounds().h)
child->setBounds(cpos);
else
child->offsetWidgets(cpos.x - child->bounds().x,
cpos.y - child->bounds().y);
}
// Change scroll bar positions
m_scrollbar_h.setPos(newScroll.x);
m_scrollbar_v.setPos(newScroll.y);
// Region to invalidate (new visible children/child parts)
Region invalidRegion(cpos);
invalidRegion &= drawableRegion;
// Move the valid screen region.
{
// The movable region includes the given "validRegion"
// intersecting itself when it's in the new position, so we don't
// overlap regions outside the "validRegion".
Region movable = validRegion;
movable.offset(delta);
movable &= validRegion;
invalidRegion -= movable; // Remove the moved region as invalid
movable.offset(-delta);
ui::move_region(manager, movable, delta.x, delta.y);
}
#ifdef DEBUG_SCROLL_EVENTS
// Paint invalid region with red fill
{
auto display = manager->getDisplay();
if (display)
display->flip(gfx::Rect(0, 0, display_w(), display_h()));
base::this_thread::sleep_for(0.002);
{
she::ScopedSurfaceLock lock(display->getSurface());
for (const auto& rc : invalidRegion)
lock->fillRect(gfx::rgba(255, 0, 0), rc);
}
if (display)
display->flip(gfx::Rect(0, 0, display_w(), display_h()));
base::this_thread::sleep_for(0.002);
}
#endif
// Don't re-invalidate the already invalid region.
if (manager)
invalidRegion -= manager->getInvalidRegion();
// Invalidate viewport's children regions
m_viewport.invalidateRegion(invalidRegion);
// Notify about the new scroll position
onScrollChange();
// Generate PaintMessages right now when the invalid region is too
// disaggregated. This is useful to avoid a lot of PaintMessage with
// small rectangles.
if (manager->getInvalidRegion().size() > 4)
flushRedraw();
}
void View::onScrollRegion(ScrollRegionEvent& ev)
{
// Do nothing
}
void View::onScrollChange()
{
// Do nothing

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2013, 2015 David Capello
// Copyright (C) 2001-2013, 2015, 2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -15,6 +15,7 @@
#include "ui/widget.h"
namespace ui {
class ScrollRegionEvent;
class View : public Widget
, public ScrollableViewDelegate {
@ -57,6 +58,8 @@ namespace ui {
void onSizeHint(SizeHintEvent& ev) override;
void onPaint(PaintEvent& ev) override;
virtual void onSetViewScroll(const gfx::Point& pt);
virtual void onScrollRegion(ScrollRegionEvent& ev);
virtual void onScrollChange();
private:

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -617,8 +617,8 @@ void Widget::setBounds(const Rect& rc)
void Widget::setBoundsQuietly(const gfx::Rect& rc)
{
m_updateRegion.offset(rc.x - m_bounds.x, rc.y - m_bounds.y);
m_bounds = rc;
invalidate();
}
void Widget::setBorder(const Border& br)
@ -874,6 +874,9 @@ void Widget::flushRedraw()
processing.push(this);
}
Manager* manager = this->manager();
ASSERT(manager);
while (!processing.empty()) {
Widget* widget = processing.front();
processing.pop();
@ -894,9 +897,9 @@ void Widget::flushRedraw()
if (!widget->m_updateRegion.isEmpty()) {
// Intersect m_updateRegion with drawable area.
{
Region region;
widget->getDrawableRegion(region, kCutTopWindows);
widget->m_updateRegion.createIntersection(widget->m_updateRegion, region);
Region drawable;
widget->getDrawableRegion(drawable, kCutTopWindows);
widget->m_updateRegion &= drawable;
}
std::size_t c, nrects = widget->m_updateRegion.size();
@ -910,9 +913,10 @@ void Widget::flushRedraw()
msg->addRecipient(widget);
// Enqueue the draw message
manager()->enqueueMessage(msg);
manager->enqueueMessage(msg);
}
manager->addInvalidRegion(widget->m_updateRegion);
widget->m_updateRegion.clear();
}
}
@ -1036,35 +1040,6 @@ void Widget::invalidateRegion(const Region& region)
onInvalidateRegion(region);
}
void Widget::scrollRegion(const Region& region, const Point& delta)
{
if (delta.x == 0 && delta.y == 0)
return;
// The movable region includes the given region in the "region"
// parameter without the invalid widget region (i.e. m_updateRegion,
// as we cannot move invalid/non-updated screen areas), and
// intersecting with the moved "region" area (so we don't overlap
// regions outside the "region" parameters)
Region movable = region;
movable.createSubtraction(movable, m_updateRegion);
movable.offset(delta);
movable.createIntersection(movable, region);
// Now we invalidate the given "region" without the moved region
// ("movable" variable).
m_updateRegion.createUnion(m_updateRegion, region);
m_updateRegion.createSubtraction(m_updateRegion, movable);
mark_dirty_flag(this);
// Move screen pixels
movable.offset(-delta);
ui::move_region(manager(), movable, delta.x, delta.y);
// Generate the kPaintMessage messages for the widget's m_updateRegion
flushRedraw();
}
class DeleteGraphicsAndSurface {
public:
DeleteGraphicsAndSurface(const gfx::Rect& clip, she::Surface* surface)

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -246,6 +246,7 @@ namespace ui {
// onResize() and want to change the size of the widget without
// generating recursive onResize() events.
void setBoundsQuietly(const gfx::Rect& rc);
void offsetWidgets(int dx, int dy);
const gfx::Size& minSize() const { return m_minSize; }
const gfx::Size& maxSize() const { return m_maxSize; }
@ -296,16 +297,15 @@ namespace ui {
void invalidateRect(const gfx::Rect& rect);
void invalidateRegion(const gfx::Region& region);
// Returns the invalid region to being updated with PaintMessages
// and onPaint() events.
// Returns the region to generate PaintMessages. It's cleared
// after flushRedraw() is called.
const gfx::Region& getUpdateRegion() const {
return m_updateRegion;
}
// Generates paint messages for the current update region.
void flushRedraw();
void scrollRegion(const gfx::Region& region, const gfx::Point& delta);
GraphicsPtr getGraphics(const gfx::Rect& clip);
// ===============================================================
@ -349,8 +349,6 @@ namespace ui {
int mnemonicChar() const;
protected:
void offsetWidgets(int dx, int dy);
// ===============================================================
// MESSAGE PROCESSING
// ===============================================================