/** * Copy dirty layer references to array for further painting. The copying is needed to not keep * lock on layers list when layers painting will happen. Dirty states of the layers are cleaned * after the copying. Layers painting can change layers state again, but it will be served on next * repaint only. */ private void copyAndCleanDirtyLayers() { if (CGraphicsQ.DEBUG) { System.err.println("[Copy dirty layers]"); } CLayer l; dirtyCount = 0; int layersCount = layers.size(); // Heuristics to increase array for copied dirty layers if (layersCount > dirtyMaxCount) { dirtyMaxCount += layersCount; dirtyLayers = new CLayer[dirtyMaxCount]; } // Copy dirty layer references and reset dirty layer states for (CLayerElement le = layers.getBottom(); le != null; le = le.getUpper()) { l = le.getLayer(); if (l.visible && l.isDirty()) { l.copyAndCleanDirtyState(); dirtyLayers[dirtyCount++] = l; } else { // !(visible && dirty) if (CGraphicsQ.DEBUG) { System.err.println("Skip Layer: " + l); } } // if } // for }
/** * First Pass: We do sweep and mark of all layers requiring a repaint, the areas behind a visible * opaque layers need no repaint */ private void sweepAndMarkLayers() { if (CGraphicsQ.DEBUG) { System.err.println("[Sweep and mark layers]"); } CLayer l; CLayerElement changed; CLayerElement le = layers.getTop(); while (le != null) { l = le.getLayer(); if (l.visible && l.opaque) { cleanLowerDirtyRegions(le); } // The dirty layer can be invisible, that means it // has been hidden since the previous paint. if (l.isDirty()) { // In the case higher layer was changed we need to // restart all the algorithm from the changed layer changed = sweepAndMarkDirtyLayer(le, !l.visible); if (changed != null) { if (CGraphicsQ.DEBUG) { System.err.println("Restart sweep and mark: " + changed.getLayer()); } le = changed; changed = null; continue; } } // Go to next lower layer le = le.getLower(); } }
/** * Propagate dirty region of the layer to other layers in the stack. The method should be called * for dirty layers only. The dirty layer can be invisible in the case it has been hidden since * the previous paint. * * <p>IMPL_NOTE: The layer been removed or set to invisible state since the previous paint is * considered as "hidden", thus it should be entirely dirty and must affect other visible layers * accordingly. * * @param le dirty layer element to be propagated to other layers * @param hidden indicates whether the dirty layer has been hidden since the previous repaint * @return the highest layer element above le with modified dirty region, or null if none */ private CLayerElement sweepAndMarkDirtyLayer(CLayerElement le, boolean hidden) { if (CGraphicsQ.DEBUG) { System.err.println("Sweep and mark dirty layer: " + le.getLayer()); } CLayer l2; CLayerElement res = null; CLayer l = le.getLayer(); // Prepare absolute dirty region coordinates of layer l int dx = l.bounds[X]; int dy = l.bounds[Y]; int dh, dw; if (l.isEmptyDirtyRegions()) { dw = l.bounds[W]; dh = l.bounds[H]; } else { dx += l.dirtyBounds[X]; dy += l.dirtyBounds[Y]; dw = l.dirtyBounds[W]; dh = l.dirtyBounds[H]; } // Sweep dirty region to upper layers for (CLayerElement le2 = le.getUpper(); le2 != null; le2 = le2.getUpper()) { l2 = le2.getLayer(); if (l2.visible) { if (l2.addDirtyRegion(dx - l2.bounds[X], dy - l2.bounds[Y], dw, dh)) { // Remember the highest changed layer res = le2; } } } // Sweep non-opaque dirty region to undelying layers if (!l.opaque || hidden) { for (CLayerElement le2 = le.getLower(); le2 != null; le2 = le2.getLower()) { l2 = le2.getLayer(); if (l2.visible) { l2.addDirtyRegion(dx - l2.bounds[X], dy - l2.bounds[Y], dw, dh); } } // A newly hidden layer should be dirty only for the first // succeeded paint, it should be cleaned as soon as underlying // layers are properly marked as dirty. if (hidden) { l.cleanDirty(); } } return res; }
/** * Sets all visible layers to dirty state. The method is needed on system events like screen * rotation, when generic layers system is not capabel to properly analyze layers changes, e.g. of * move/resize kind. It could be fixed in the future and this method will be out of use. */ public void setAllDirty() { synchronized (layers) { CLayer l; for (CLayerElement le = layers.getBottom(); le != null; le = le.getUpper()) { l = le.getLayer(); if (l.visible) { l.addDirtyRegion(); } // if } // for } // synchronized }
/** * Handle input from some type of device-dependent input method. This could be input from * something such as T9, or a phonebook lookup, etc. * * @param str the text to handle as direct input * @return true if this window or one of its layers processed the event */ public boolean methodInput(String str) { CLayer layer; synchronized (layers) { for (CLayerElement le = layers.getTop(); le != null; le = le.getLower()) { layer = le.getLayer(); if (layer.visible && layer.supportsInput && layer.methodInput(str)) { return true; } } } // sync return false; }
/** * Allow this window to process key input. The type of key input will be press, release, repeat, * etc. The key code will identify which key generated the event. This method will return true if * the event was processed by this window or one of its layers, false otherwise. * * @param type the type of key event (press, release, repeat) * @param keyCode the identifier of the key which generated the event * @return true if this window or one of its layers processed the event */ public boolean keyInput(int type, int keyCode) { CLayer layer; synchronized (layers) { for (CLayerElement le = layers.getTop(); le != null; le = le.getLower()) { layer = le.getLayer(); if (layer.supportsInput && layer.keyInput(type, keyCode)) { return true; } } } // sync return false; }
/** * Check whether layer is overlapped with a higher visible layer in the layer stack of the window * * @param l layer to check overlapping * @return true if overlapped, false otherwise */ public boolean isOverlapped(CLayer l) { synchronized (layers) { CLayerElement le = layers.find(l); if (le != null) { CLayer l2; for (le = le.getUpper(); le != null; le = le.getUpper()) { l2 = le.getLayer(); if (l2.isVisible() && l.intersects(l2)) { return true; } } } } return false; }
/** * Allow this window to process pointer input. The type of pointer input will be press, release, * drag, etc. The x and y coordinates will identify the point at which the pointer event occurred * in the coordinate system of this window. This window will translate the coordinates * appropriately for each layer contained in this window. This method will return true if the * event was processed by this window or one of its layers, false otherwise. * * @param type the type of pointer event (press, release, drag) * @param x the x coordinate of the location of the event * @param y the y coordinate of the location of the event * @return true if this window or one of its layers processed the event */ public boolean pointerInput(int type, int x, int y) { CLayer layer; synchronized (layers) { for (CLayerElement le = layers.getTop(); le != null; le = le.getLower()) { layer = le.getLayer(); if (layer.visible && layer.supportsInput && layer.handlePoint(x, y)) { // If the layer is visible, supports input, and // contains the point of the pointer press, we translate // the point into the layer's coordinate space and // pass on the input if (layer.pointerInput(type, x - layer.bounds[X], y - layer.bounds[Y])) { return true; } } } } // sync return false; }
/** * Subtract this layer area from an underlying dirty regions. The method is designed to reduce * dirty regions of a layres below the opaque visible layer. * * @param le layer list element */ private void cleanLowerDirtyRegions(CLayerElement le) { if (CGraphicsQ.DEBUG) { System.err.println("Clean dirty regions under opaque layer: " + le.getLayer()); } CLayer l = le.getLayer(); for (CLayerElement le2 = le.getLower(); le2 != null; le2 = le2.getLower()) { CLayer l2 = le2.getLayer(); if (l2.isDirty()) { l2.subDirtyRegion( l.bounds[X] - l2.bounds[X], l.bounds[Y] - l2.bounds[Y], l.bounds[W], l.bounds[H]); } } }