[aseprite] 187/308: Generalize View::setViewScroll() to blit valid/scrollable region automatically

Tobias Hansen thansen at moszumanska.debian.org
Tue Mar 8 02:45:09 UTC 2016


This is an automated email from the git hooks/post-receive script.

thansen pushed a commit to branch master
in repository aseprite.

commit b0650f6afea1b860a8593fd4614ca272f8edbfac
Author: David Capello <davidcapello at gmail.com>
Date:   Tue Jan 5 16:37:52 2016 -0300

    Generalize View::setViewScroll() to blit valid/scrollable region automatically
    
    With this change we are removing specific code to scroll the Editor widget.
    Now if we use Editor::setEditorScroll() to scroll it should work as
    View::setViewScroll(). In this way we remove the ugly "blitValidRegion"
    parameter from setEditorScroll().
    
    Changes:
    * Removed Widget::scrollRegion() because each widget must control
      valid/invalid regions after a ui::move_region() in a very specific way
      (see View::setViewScroll() or Window::moveWindow)
    * Invalidate the whole widget on Widget::setBoundsQuietly()
    * Fixed problems blitting invalid regions/not yet updated/painted: using
      the new ui::Manager::m_invalidRegion.
    * Added View::onSetViewScroll() and View::onScrollRegion()
    * Added FileListView to avoid moving the thumbnail region when we scroll
---
 data/widgets/file_selector.xml                  |   4 +-
 src/app/CMakeLists.txt                          |   3 +-
 src/app/commands/cmd_scroll.cpp                 |   2 +-
 src/app/ui/editor/drawing_state.cpp             |   2 +-
 src/app/ui/editor/editor.cpp                    |  51 +++-----
 src/app/ui/editor/editor.h                      |   5 +-
 src/app/ui/editor/editor_view.cpp               |  31 ++++-
 src/app/ui/editor/editor_view.h                 |   4 +-
 src/app/ui/editor/moving_pixels_state.cpp       |   2 +-
 src/app/ui/editor/scrolling_state.cpp           |   4 +-
 src/app/ui/editor/state_with_wheel_behavior.cpp |   2 +-
 src/app/ui/file_list.cpp                        |  64 ++++++---
 src/app/ui/file_list.h                          |  10 +-
 src/app/ui/file_list_view.cpp                   |  35 +++++
 src/app/ui/file_list_view.h                     |  26 ++++
 src/app/ui/file_selector.cpp                    |   9 +-
 src/app/ui/file_selector.h                      |   4 +-
 src/ui/manager.cpp                              |   7 +
 src/ui/manager.h                                |  16 ++-
 src/ui/scroll_region_event.h                    |  30 +++++
 src/ui/view.cpp                                 | 166 +++++++++++++++++++++---
 src/ui/view.h                                   |   5 +-
 src/ui/widget.cpp                               |  39 +-----
 src/ui/widget.h                                 |   2 -
 24 files changed, 395 insertions(+), 128 deletions(-)

diff --git a/data/widgets/file_selector.xml b/data/widgets/file_selector.xml
index ccaa288..8fc3b7c 100644
--- a/data/widgets/file_selector.xml
+++ b/data/widgets/file_selector.xml
@@ -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" />
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 275655b..cbbed1f 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -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
diff --git a/src/app/commands/cmd_scroll.cpp b/src/app/commands/cmd_scroll.cpp
index cff9e56..e987971 100644
--- a/src/app/commands/cmd_scroll.cpp
+++ b/src/app/commands/cmd_scroll.cpp
@@ -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
diff --git a/src/app/ui/editor/drawing_state.cpp b/src/app/ui/editor/drawing_state.cpp
index a20d922..f2bb12b 100644
--- a/src/app/ui/editor/drawing_state.cpp
+++ b/src/app/ui/editor/drawing_state.cpp
@@ -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));
 
diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp
index a0ba039..456419b 100644
--- a/src/app/ui/editor/editor.cpp
+++ b/src/app/ui/editor/editor.cpp
@@ -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();
diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h
index df138c9..479483c 100644
--- a/src/app/ui/editor/editor.h
+++ b/src/app/ui/editor/editor.h
@@ -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();
diff --git a/src/app/ui/editor/editor_view.cpp b/src/app/ui/editor/editor_view.cpp
index d59b859..6516e7b 100644
--- a/src/app/ui/editor/editor_view.cpp
+++ b/src/app/ui/editor/editor_view.cpp
@@ -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();
diff --git a/src/app/ui/editor/editor_view.h b/src/app/ui/editor/editor_view.h
index 236cae6..e7a9321 100644
--- a/src/app/ui/editor/editor_view.h
+++ b/src/app/ui/editor/editor_view.h
@@ -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:
diff --git a/src/app/ui/editor/moving_pixels_state.cpp b/src/app/ui/editor/moving_pixels_state.cpp
index 209791d..9f3e03f 100644
--- a/src/app/ui/editor/moving_pixels_state.cpp
+++ b/src/app/ui/editor/moving_pixels_state.cpp
@@ -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);
diff --git a/src/app/ui/editor/scrolling_state.cpp b/src/app/ui/editor/scrolling_state.cpp
index 84c2720..3bd9ba6 100644
--- a/src/app/ui/editor/scrolling_state.cpp
+++ b/src/app/ui/editor/scrolling_state.cpp
@@ -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;
 }
 
diff --git a/src/app/ui/editor/state_with_wheel_behavior.cpp b/src/app/ui/editor/state_with_wheel_behavior.cpp
index 4627c8a..50b83fc 100644
--- a/src/app/ui/editor/state_with_wheel_behavior.cpp
+++ b/src/app/ui/editor/state_with_wheel_behavior.cpp
@@ -153,7 +153,7 @@ bool StateWithWheelBehavior::onMouseWheel(Editor* editor, MouseMessage* msg)
         }
       }
 
-      editor->setEditorScroll(scroll+delta, true);
+      editor->setEditorScroll(scroll+delta);
       break;
     }
 
diff --git a/src/app/ui/file_list.cpp b/src/app/ui/file_list.cpp
index 75a026f..006ebbc 100644
--- a/src/app/ui/file_list.cpp
+++ b/src/app/ui/file_list.cpp
@@ -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) {
diff --git a/src/app/ui/file_list.h b/src/app/ui/file_list.h
index bbefc2c..8e4a8cc 100644
--- a/src/app/ui/file_list.h
+++ b/src/app/ui/file_list.h
@@ -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
diff --git a/src/app/ui/file_list_view.cpp b/src/app/ui/file_list_view.cpp
new file mode 100644
index 0000000..be0378d
--- /dev/null
+++ b/src/app/ui/file_list_view.cpp
@@ -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
diff --git a/src/app/ui/file_list_view.h b/src/app/ui/file_list_view.h
new file mode 100644
index 0000000..e1c722a
--- /dev/null
+++ b/src/app/ui/file_list_view.h
@@ -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
diff --git a/src/app/ui/file_selector.cpp b/src/app/ui/file_selector.cpp
index b9838d9..63e6bdc 100644
--- a/src/app/ui/file_selector.cpp
+++ b/src/app/ui/file_selector.cpp
@@ -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));
diff --git a/src/app/ui/file_selector.h b/src/app/ui/file_selector.h
index fde96c5..6a1a67a 100644
--- a/src/app/ui/file_selector.h
+++ b/src/app/ui/file_selector.h
@@ -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
diff --git a/src/ui/manager.cpp b/src/ui/manager.cpp
index 0f2015c..1ab31dd 100644
--- a/src/ui/manager.cpp
+++ b/src/ui/manager.cpp
@@ -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 {
diff --git a/src/ui/manager.h b/src/ui/manager.h
index bc91410..0b603cc 100644
--- a/src/ui/manager.h
+++ b/src/ui/manager.h
@@ -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.
diff --git a/src/ui/scroll_region_event.h b/src/ui/scroll_region_event.h
new file mode 100644
index 0000000..5b5a95b
--- /dev/null
+++ b/src/ui/scroll_region_event.h
@@ -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
diff --git a/src/ui/view.cpp b/src/ui/view.cpp
index 754e6b6..b625dca 100644
--- a/src/ui/view.cpp
+++ b/src/ui/view.cpp
@@ -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
diff --git a/src/ui/view.h b/src/ui/view.h
index f97d40a..f07efe4 100644
--- a/src/ui/view.h
+++ b/src/ui/view.h
@@ -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:
diff --git a/src/ui/widget.cpp b/src/ui/widget.cpp
index 3ebcea2..231ff13 100644
--- a/src/ui/widget.cpp
+++ b/src/ui/widget.cpp
@@ -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();
@@ -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)
diff --git a/src/ui/widget.h b/src/ui/widget.h
index 092290b..54a9c2a 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -306,8 +306,6 @@ namespace ui {
     // 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);
 
     // ===============================================================

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/aseprite.git



More information about the Pkg-games-commits mailing list