Skip to content

Commit

Permalink
Vertical spacing: move elements above line when holding Ctrl
Browse files Browse the repository at this point in the history
  • Loading branch information
Technius committed Feb 11, 2022
1 parent 8d9b722 commit baefd26
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 28 deletions.
118 changes: 92 additions & 26 deletions src/core/control/tools/VerticalToolHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,28 @@
#include <cmath>
#include <memory>

#include <cairo.h>

#include "model/Layer.h"
#include "undo/UndoRedoHandler.h"
#include "view/DocumentView.h"

VerticalToolHandler::VerticalToolHandler(Redrawable* view, const PageRef& page, Settings* settings, double y,
double zoom):
view(view), page(page), layer(this->page->getSelectedLayer()), snappingHandler(settings) {
bool initiallyReverse, double zoom):
view(view),
page(page),
layer(this->page->getSelectedLayer()),
spacingSide(initiallyReverse ? Side::Above : Side::Below),
snappingHandler(settings) {
double ySnapped = snappingHandler.snapVertically(y, false);
this->startY = ySnapped;
this->endY = ySnapped;
for (Element* e: this->layer->getElements()) {
if (e->getY() >= y) {
this->elements.push_back(e);
}
}
this->zoom = zoom;
this->crBuffer = cairo_image_surface_create(
CAIRO_FORMAT_ARGB32, static_cast<int>(this->page->getWidth() * this->zoom),
static_cast<int>(std::max(this->startY, this->page->getHeight() - this->startY) * this->zoom));

for (Element* e: this->elements) {
this->layer->removeElement(e, false);
}

this->crBuffer = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, this->page->getWidth() * zoom,
(this->page->getHeight() - y) * zoom);

cairo_t* cr = cairo_create(this->crBuffer);
cairo_scale(cr, zoom, zoom);
cairo_translate(cr, 0, -y);
DocumentView v;
v.drawSelection(cr, this);

cairo_destroy(cr);
this->adoptElements(this->spacingSide);

view->rerenderPage();
}
Expand All @@ -46,6 +38,46 @@ VerticalToolHandler::~VerticalToolHandler() {
}
}

void VerticalToolHandler::adoptElements(const Side side) {
this->spacingSide = side;

// Return current elements back to page
for (Element* e: this->elements) { this->layer->addElement(e); }
this->elements.clear();

// Add new elements based on position
for (Element* e: this->layer->getElements()) {
if ((side == Side::Below && e->getY() >= this->startY) ||
(side == Side::Above && e->getY() + e->getElementHeight() <= this->startY)) {
this->elements.push_back(e);
}
}

for (Element* e: this->elements) { this->layer->removeElement(e, false); }

cairo_t* cr = cairo_create(this->crBuffer);
cairo_scale(cr, this->zoom, this->zoom);

// Clear the buffer first
cairo_save(cr);
cairo_set_source_rgba(cr, 0, 0, 0, 0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr);
cairo_restore(cr);

// if below, render elements translated so that startY is 0.
// if above, 0 is already the top of the page.
if (this->spacingSide == Side::Below) {
cairo_translate(cr, 0, -this->startY);
} else {
g_assert(this->spacingSide == Side::Above);
}
DocumentView v;
v.drawSelection(cr, this);

cairo_destroy(cr);
}

void VerticalToolHandler::paint(cairo_t* cr, GdkRectangle* rect, double zoom) {
GdkRGBA selectionColor = view->getSelectionColor();

Expand All @@ -54,29 +86,63 @@ void VerticalToolHandler::paint(cairo_t* cr, GdkRectangle* rect, double zoom) {
gdk_cairo_set_source_rgba(cr, &selectionColor);

const double y = std::min(this->startY, this->endY);
const double height = std::abs(this->startY - this->endY);
const double dy = this->endY - this->startY;

cairo_rectangle(cr, 0, y * zoom, this->page->getWidth() * zoom, height * zoom);
cairo_rectangle(cr, 0, y * zoom, this->page->getWidth() * zoom, std::abs(dy) * zoom);

cairo_stroke_preserve(cr);
auto applied = GdkRGBA{selectionColor.red, selectionColor.green, selectionColor.blue, 0.3};
gdk_cairo_set_source_rgba(cr, &applied);
cairo_fill(cr);

cairo_set_source_surface(cr, this->crBuffer, 0, this->endY * zoom);

const double elemY = (this->spacingSide == Side::Below ? this->endY : dy) * zoom;
cairo_set_source_surface(cr, this->crBuffer, 0, elemY);
cairo_paint(cr);

#ifdef DEBUG_SHOW_PAINT_BOUNDS
cairo_rectangle(cr, 0, elemY, cairo_image_surface_get_width(this->crBuffer),
cairo_image_surface_get_height(this->crBuffer));
cairo_set_source_rgba(cr, 1.0, 0, 0, 0.3);
cairo_fill(cr);
#endif
}

void VerticalToolHandler::currentPos(double x, double y) {
double ySnapped = snappingHandler.snapVertically(y, false);
if (this->endY == ySnapped) {
return;
}
double y1 = std::min(this->endY, ySnapped);

const double oldEnd = this->endY;
this->endY = ySnapped;

this->view->repaintRect(0, y1, this->page->getWidth(), this->page->getHeight());
if (this->spacingSide == Side::Below) {
this->view->rerenderRect(0, std::min(oldEnd, ySnapped), this->page->getWidth(), this->page->getHeight());
} else {
g_assert(this->spacingSide == Side::Above);
this->view->rerenderRect(0, 0, this->page->getWidth(), std::max(oldEnd, ySnapped));
}
}

bool VerticalToolHandler::onKeyPressEvent(GdkEventKey* event) {
if ((event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R) &&
this->spacingSide == Side::Below) {
this->adoptElements(Side::Above);
this->view->rerenderPage();
return true;
}
return false;
}

bool VerticalToolHandler::onKeyReleaseEvent(GdkEventKey* event) {
if ((event->keyval == GDK_KEY_Control_L || event->keyval == GDK_KEY_Control_R) &&
this->spacingSide == Side::Above) {
this->adoptElements(Side::Below);
this->view->rerenderPage();
return true;
}
return false;
}

auto VerticalToolHandler::getElements() -> std::vector<Element*>* { return &this->elements; }
Expand Down
52 changes: 51 additions & 1 deletion src/core/control/tools/VerticalToolHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,81 @@

class VerticalToolHandler: public ElementContainer {
public:
VerticalToolHandler(Redrawable* view, const PageRef& page, Settings* settings, double y, double zoom);
/**
* @param initiallyReverse Set this to true if the user has the reverse mode
* button (e.g., Ctrl) held down when a vertical selection is started.
*/
VerticalToolHandler(Redrawable* view, const PageRef& page, Settings* settings, double y, bool initiallyReverse,
double zoom);
~VerticalToolHandler() override;
VerticalToolHandler(VerticalToolHandler&) = delete;
VerticalToolHandler& operator=(VerticalToolHandler&) = delete;
VerticalToolHandler(VerticalToolHandler&&) = delete;
VerticalToolHandler&& operator=(VerticalToolHandler&&) = delete;

void paint(cairo_t* cr, GdkRectangle* rect, double zoom);

/** Update the tool state with the new spacing position */
void currentPos(double x, double y);

bool onKeyPressEvent(GdkEventKey* event);
bool onKeyReleaseEvent(GdkEventKey* event);

std::unique_ptr<MoveUndoAction> finalize();

std::vector<Element*>* getElements() override;

private:
enum class Side {
/** elements above the reference line */
Above = -1,
/** elements below the reference line */
Below = 1,
};

/**
* Clear the currently moved elements, and then select all elements
* above/below startY (depending on the side) to use for the spacing.
* Lastly, redraw the elements to the buffer.
*/
void adoptElements(Side side);

/**
* Recreate the buffer if the new zoom value is higher.
*/
void updateZoom(double newZoom);

/**
* Clear the buffer and redraw the elements being spaced.
*/
void redrawBuffer();

GdkWindow* window;
Redrawable* view = nullptr;
PageRef page;
Layer* layer = nullptr;
std::vector<Element*> elements;

/**
* Image buffer containing a rendering of the elements being spaced. This
* buffer is rendered below the endY if direction is below, or above the
* endY if the direction is above.
*/
cairo_surface_t* crBuffer = nullptr;

double startY = 0;
double endY = 0;

/**
* Indicates whether to move elements above (negative) or below (positive) the anchor line.
*/
Side spacingSide;

/**
* Current zoom level.
*/
double zoom; // TODO: listen to zoom change in case zoom is changed during spacing

/**
* The handler for snapping points
*/
Expand Down
10 changes: 9 additions & 1 deletion src/core/gui/PageView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ auto XojPageView::onButtonPressEvent(const PositionInputData& pos) -> bool {
this->inEraser = true;
} else if (h->getToolType() == TOOL_VERTICAL_SPACE) {
g_assert_null(this->verticalSpace);
this->verticalSpace = new VerticalToolHandler(this, this->page, this->settings, y, zoom);
this->verticalSpace = new VerticalToolHandler(this, this->page, this->settings, y, pos.isControlDown(), zoom);
} else if (h->getToolType() == TOOL_SELECT_RECT || h->getToolType() == TOOL_SELECT_REGION ||
h->getToolType() == TOOL_PLAY_OBJECT || h->getToolType() == TOOL_SELECT_OBJECT ||
h->getToolType() == TOOL_SELECT_PDF_TEXT_LINEAR || h->getToolType() == TOOL_SELECT_PDF_TEXT_RECT) {
Expand Down Expand Up @@ -644,6 +644,10 @@ auto XojPageView::onKeyPressEvent(GdkEventKey* event) -> bool {
return this->inputHandler->onKeyEvent(event);
}

if (this->verticalSpace) {
return this->verticalSpace->onKeyPressEvent(event);
}


return false;
}
Expand All @@ -665,6 +669,10 @@ auto XojPageView::onKeyReleaseEvent(GdkEventKey* event) -> bool {
return true;
}

if (this->verticalSpace && this->verticalSpace->onKeyReleaseEvent(event)) {
return true;
}

return false;
}

Expand Down

0 comments on commit baefd26

Please sign in to comment.