// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.collide.client.editor.renderer;
import com.google.collide.client.Resources;
import com.google.collide.client.editor.Buffer;
import com.google.collide.client.editor.Editor;
import com.google.collide.client.editor.FocusManager;
import com.google.collide.client.editor.ViewportModel;
import com.google.collide.client.editor.gutter.Gutter;
import com.google.collide.client.editor.renderer.ChangeTracker.ChangeType;
import com.google.collide.client.editor.selection.SelectionModel;
import com.google.collide.client.util.logging.Log;
import com.google.collide.shared.document.Document;
import com.google.collide.shared.document.Line;
import com.google.collide.shared.util.ListenerManager;
import com.google.collide.shared.util.ListenerRegistrar;
import java.util.EnumSet;
/**
* A class that is the entry point for the rendering of the editor.
*
* The lifecycle of this class is tied to the current document. If the document
* is replaced, a new instance of this class is created for the new document.
*
*/
public class Renderer {
public static Renderer create(Document document, ViewportModel viewport,
Buffer buffer, Gutter leftGutter, SelectionModel selection, FocusManager focusManager,
Editor editor, Resources res, RenderTimeExecutor renderTimeExecutor) {
return new Renderer(document, viewport, buffer, leftGutter, selection, focusManager,
editor, res, renderTimeExecutor);
}
/**
* Listener that is notified when the rendering is finished.
*/
public interface CompletionListener {
void onRenderCompleted();
}
/**
* Listener that is notified on creation and garbage collection of a
* rendered line.
*/
public interface LineLifecycleListener {
void onRenderedLineCreated(Line line, int lineNumber);
void onRenderedLineGarbageCollected(Line line);
void onRenderedLineShifted(Line line, int lineNumber);
}
private static final boolean ENABLE_PROFILING = false;
private final ChangeTracker changeTracker;
private final ListenerManager<CompletionListener> completionListenerManager;
private final ListenerManager<LineLifecycleListener> lineLifecycleListenerManager;
private final LineNumberRenderer lineNumberRenderer;
private final ListenerManager.Dispatcher<CompletionListener> renderCompletedDispatcher =
new ListenerManager.Dispatcher<CompletionListener>() {
@Override
public void dispatch(CompletionListener listener) {
listener.onRenderCompleted();
}
};
private final ViewportRenderer viewportRenderer;
private final ViewportModel viewport;
private final RenderTimeExecutor renderTimeExecutor;
private Renderer(Document document, ViewportModel viewport, Buffer buffer,
Gutter leftGutter, SelectionModel selection, FocusManager focusManager,
Editor editor, Resources res, RenderTimeExecutor renderTimeExecutor) {
this.viewport = viewport;
this.renderTimeExecutor = renderTimeExecutor;
this.completionListenerManager = ListenerManager.create();
this.lineLifecycleListenerManager = ListenerManager.create();
this.changeTracker =
new ChangeTracker(this, buffer, document, viewport, selection, focusManager);
this.viewportRenderer = new ViewportRenderer(
document, buffer, viewport, editor.getView(), lineLifecycleListenerManager);
this.lineNumberRenderer = new LineNumberRenderer(buffer, res, leftGutter, viewport, selection,
editor);
}
public void addLineRenderer(LineRenderer lineRenderer) {
viewportRenderer.addLineRenderer(lineRenderer);
}
public ListenerRegistrar<CompletionListener> getCompletionListenerRegistrar() {
return completionListenerManager;
}
public ListenerRegistrar<LineLifecycleListener> getLineLifecycleListenerRegistrar() {
return lineLifecycleListenerManager;
}
public void removeLineRenderer(LineRenderer lineRenderer) {
viewportRenderer.removeLineRenderer(lineRenderer);
}
public void renderAll() {
viewportRenderer.render();
renderTimeExecutor.executeQueuedCommands();
handleRenderCompleted();
}
public void renderChanges() {
if (ENABLE_PROFILING) {
Log.markTimeline(getClass(), "Rendering changes...");
}
EnumSet<ChangeType> changes = changeTracker.getChanges();
int viewportTopmostContentChangedLine =
Math.max(viewport.getTopLineNumber(), changeTracker.getTopmostContentChangedLineNumber());
if (changes.contains(ChangeType.VIEWPORT_LINE_NUMBER)) {
if (ENABLE_PROFILING) {
Log.markTimeline(getClass(), " - lineNumberRenderer...");
}
lineNumberRenderer.render();
if (ENABLE_PROFILING) {
Log.markTimeline(getClass(), " - renderViewportLineNumbersChanged...");
}
viewportRenderer.renderViewportLineNumbersChanged(changeTracker
.getViewportLineNumberChangedEdges());
}
if (changes.contains(ChangeType.VIEWPORT_CONTENT)) {
if (ENABLE_PROFILING) {
Log.markTimeline(getClass(), " - renderViewportContentChange...");
}
viewportRenderer.renderViewportContentChange(viewportTopmostContentChangedLine,
changeTracker.getViewportRemovedLines());
if (changeTracker.hadContentChangeThatUpdatesFollowingLines()) {
if (ENABLE_PROFILING) {
Log.markTimeline(getClass(), " - renderLineAndFollowing...");
}
lineNumberRenderer.renderLineAndFollowing(viewportTopmostContentChangedLine);
}
}
if (changes.contains(ChangeType.VIEWPORT_SHIFT)) {
if (ENABLE_PROFILING) {
Log.markTimeline(getClass(), " - renderViewportShift...");
}
viewportRenderer.renderViewportShift(false);
lineNumberRenderer.render();
}
if (changes.contains(ChangeType.DIRTY_LINE)) {
if (ENABLE_PROFILING) {
Log.markTimeline(getClass(), " - renderDirtyLines...");
}
viewportRenderer.renderDirtyLines(changeTracker.getDirtyLines());
}
renderTimeExecutor.executeQueuedCommands();
handleRenderCompleted();
if (ENABLE_PROFILING) {
Log.markTimeline(getClass(), " - Done rendering changes");
}
}
private void handleRenderCompleted() {
viewportRenderer.handleRenderCompleted();
completionListenerManager.dispatch(renderCompletedDispatcher);
}
public void requestRenderLine(Line line) {
changeTracker.requestRenderLine(line);
}
public void teardown() {
changeTracker.teardown();
viewportRenderer.teardown();
lineNumberRenderer.teardown();
}
}