mirror of https://github.com/aseprite/aseprite.git
				
				
				
			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
This commit is contained in:
		
							parent
							
								
									1260cf12c5
								
							
						
					
					
						commit
						b0650f6afe
					
				|  | @ -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" /> | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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)); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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: | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -153,7 +153,7 @@ bool StateWithWheelBehavior::onMouseWheel(Editor* editor, MouseMessage* msg) | |||
|         } | ||||
|       } | ||||
| 
 | ||||
|       editor->setEditorScroll(scroll+delta, true); | ||||
|       editor->setEditorScroll(scroll+delta); | ||||
|       break; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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
 | ||||
|  | @ -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 | ||||
|  | @ -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)); | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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.
 | ||||
|  |  | |||
|  | @ -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 | ||||
							
								
								
									
										166
									
								
								src/ui/view.cpp
								
								
								
								
							
							
						
						
									
										166
									
								
								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
 | ||||
|  |  | |||
|  | @ -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: | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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); | ||||
| 
 | ||||
|     // ===============================================================
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue