Project

General

Profile

Feature #4358 » 0001-Add-support-for-zoom-pan-map-area-updates.patch

Bruce Toll, 07/29/2015 01:42 AM

View differences:

src/Wt/Chart/WCartesianChart
* \note Client side interaction is only available if the chart is drawn
* on an HTML canvas. This is the default rendering method on modern browsers.
* \note Some features are currently not supported in interactive mode:
* - Tooltips are not zoomed or panned along with the chart
* - Axes set at ZeroValue position will not always be drawn correctly.
* They may be clipped off outside of the chart area, and when zooming,
* the axis ticks will change size.
......
WTransform combinedTransform() const;
WTransform combinedTransform(WTransform xTransform, WTransform yTransform) const;
void setInitialZoomAndPan();
void addAreaMask();
WPointF hv(double x, double y, double width) const;
WPointF inverseHv(double x, double y, double width) const;
src/Wt/Chart/WCartesianChart.C
const WModelIndex& xIndex,
WAbstractArea *area)
{
if (areas().empty())
addAreaMask();
addArea(area);
}
......
}
}
void WCartesianChart::addAreaMask()
{
WRectF all = hv(WRectF(0, 0, width_, height_));
WRectF chart = hv(chartArea_);
std::vector<WRectF> rects;
rects.push_back(WRectF(all.topLeft(), WPointF(all.right(), chart.top())));
rects.push_back(WRectF(WPointF(all.left(), chart.bottom()), all.bottomRight()));
rects.push_back(WRectF(WPointF(all.left(), chart.top()), chart.bottomLeft()));
rects.push_back(WRectF(chart.topRight(), WPointF(all.right(), chart.bottom())));
for (int i = 0; i < rects.size(); ++i) {
if (rects[i].height() > 0 && rects[i].width() > 0) {
WRectArea *rect = new WRectArea(rects[i]);
rect->setHole(true);
rect->setTransformable(false);
addArea(rect);
}
}
}
}
}
src/Wt/WAbstractArea
*/
bool isHole() const { return hole_; }
void setTransformable(bool transformable);
bool isTransformable() const { return transformable_; }
/*! \brief Sets a link.
*
* By setting a link, the area behaves like a WAnchor.
......
};
bool hole_;
bool transformable_;
AnchorImpl *anchor_;
void createAnchorImpl();
......
WAbstractArea();
virtual bool updateDom(DomElement& element, bool all);
virtual std::string updateAreaCoordsJS() = 0;
void repaint();
std::string jsRef() { return impl()->jsRef(); }
private:
WInteractWidget *impl();
......
friend class WImage;
friend class Impl::AreaWidget;
friend class WPaintedWidget;
};
}
src/Wt/WAbstractArea.C
WAbstractArea::WAbstractArea()
: impl_(new Impl::AreaWidget(this)),
hole_(false),
transformable_(true),
anchor_(0)
{ }
......
repaint();
}
void WAbstractArea::setTransformable(bool transformable)
{
transformable_ = transformable;
repaint();
}
void WAbstractArea::setLink(const WLink& link)
{
createAnchorImpl();
src/Wt/WCircleArea
protected:
virtual bool updateDom(DomElement& element, bool all);
virtual std::string updateAreaCoordsJS() WT_CXX11ONLY(override);
};
}
src/Wt/WCircleArea.C
return WAbstractArea::updateDom(element, all);
}
std::string WCircleArea::updateAreaCoordsJS()
{
std::stringstream coords;
coords << "[" << jsRef() << ",["
<< x_ << ',' << y_ << ',' << r_ << "]]";
return coords.str();
}
}
src/Wt/WImage
*/
EventSignal<>& imageLoaded();
void setTargetJS(std::string targetJS);
virtual std::string updateAreasJS();
virtual std::string setAreaCoordsJS();
private:
static const char *LOAD_SIGNAL;
......
WLink imageLink_;
Impl::MapWidget *map_;
std::bitset<3> flags_;
std::string targetJS_;
void resourceChanged();
......
virtual void getDomChanges(std::vector<DomElement *>& result,
WApplication *app);
virtual void updateDom(DomElement& element, bool all);
virtual void defineJavaScript();
virtual void render(WFlags<RenderFlag> flags);
virtual DomElementType domElementType() const;
virtual void propagateRenderOk(bool deep);
virtual std::string updateAreaCoordsJSON() const;
friend class WLabel;
friend class Impl::MapWidget;
static std::vector<WAbstractArea *> noAreas_;
};
src/Wt/WImage.C
#include "DomElement.h"
#ifndef WT_DEBUG_JS
#include "js/WImage.min.js"
#endif
namespace Wt {
LOGGER("WImage");
......
MapWidget() { }
protected:
virtual void render(WFlags<RenderFlag> flags)
{
WContainerWidget::render(flags);
WImage *parent_img = dynamic_cast<WImage *>(parent());
if (!parent_img->targetJS_.empty())
parent_img->doJavaScript(parent_img->setAreaCoordsJS());
}
virtual void updateDom(DomElement& element, bool all)
{
if (all)
......
WInteractWidget::propagateRenderOk(deep);
}
void WImage::defineJavaScript()
{
WApplication *app = WApplication::instance();
LOAD_JAVASCRIPT(app, "js/WImage.js", "WImage", wtjs1);
WStringStream ss;
ss << "new " WT_CLASS ".WImage("
<< app->javaScriptClass() << "," << jsRef() << "," << targetJS_ << ");";
doJavaScript(ss.str());
}
void WImage::render(WFlags<RenderFlag> flags)
{
if (flags & RenderFull) {
if (!targetJS_.empty())
defineJavaScript();
}
WInteractWidget::render(flags);
}
DomElementType WImage::domElementType() const
{
return map_ ? DomElement_SPAN : DomElement_IMG;
}
void WImage::setTargetJS(std::string targetJS)
{
targetJS_ = targetJS;
}
std::string WImage::updateAreasJS()
{
WStringStream ss;
if (!targetJS_.empty()) {
ss << "jQuery.data(" << jsRef() << ", 'obj').updateAreas();";
}
return ss.str();
}
std::string WImage::setAreaCoordsJS()
{
WStringStream ss;
if (!targetJS_.empty()) {
ss << "jQuery.data(" << jsRef() << ", 'obj').setAreaCoordsJSON("
<< updateAreaCoordsJSON() << ");";
}
return ss.str();
}
std::string WImage::updateAreaCoordsJSON() const
{
WStringStream js;
const std::vector<WAbstractArea *> &areas = this->areas();
if (!areas.empty()) {
for (int i = 0; i < areas.size(); ++i) {
if (areas[i]->isTransformable()) {
if (js.empty())
js << "[";
else
js << ",";
js << areas[i]->updateAreaCoordsJS();
}
}
js << "]";
}
return js.str();
}
}
src/Wt/WPaintedWidget.C
#include <boost/lexical_cast.hpp>
#include "Wt/WAbstractArea"
#include "Wt/WApplication"
#include "Wt/WCanvasPaintDevice"
#include "Wt/WEnvironment"
......
WStringStream ss;
ss << widget_->objJsRef() << ".repaint=function(){";
ss << canvasDevice->recordedJs_.str();
if (widget_->areaImage_) {
widget_->areaImage_->setTargetJS(widget_->objJsRef());
ss << widget_->areaImage_->updateAreasJS();
}
ss << "};";
el->callJavaScript(ss.str());
}
......
WStringStream ss;
ss << widget_->objJsRef() << ".repaint=function(){";
ss << canvasDevice->recordedJs_.str();
if (widget_->areaImage_) {
widget_->areaImage_->setTargetJS(widget_->objJsRef());
ss << widget_->areaImage_->updateAreasJS();
}
ss << "};";
el->callJavaScript(ss.str());
}
src/Wt/WPolygonArea
protected:
virtual bool updateDom(DomElement& element, bool all);
virtual std::string updateAreaCoordsJS() WT_CXX11ONLY(override);
};
}
src/Wt/WPolygonArea.C
return WAbstractArea::updateDom(element, all);
}
std::string WPolygonArea::updateAreaCoordsJS()
{
std::stringstream coords;
coords << "[" << jsRef() << ",[";
for (unsigned i = 0; i < points_.size(); ++i) {
if (i != 0)
coords << ',';
coords << points_[i].x() << ',' << points_[i].y();
}
coords << "]]";
return coords.str();
}
}
src/Wt/WRectArea
protected:
virtual bool updateDom(DomElement& element, bool all);
virtual std::string updateAreaCoordsJS() WT_CXX11ONLY(override);
};
}
src/Wt/WRectArea.C
return WAbstractArea::updateDom(element, all);
}
std::string WRectArea::updateAreaCoordsJS()
{
std::stringstream coords;
coords << "[" << jsRef() << ",["
<< x_ << ',' << y_ << ',' << (x_ + width_) << ',' << (y_ + height_) << "]]";
return coords.str();
}
}
src/js/WCartesianChart.js
return mult([1,0,0,-1,l,b], mult(transform(X), mult(transform(Y), [1,0,0,-1,-l,b])));
}
}
target.combinedTransform = combinedTransform;
function transformedChartArea() {
return mult(combinedTransform(), config.area);
src/js/WImage.js
/*
* Copyright (C) 2015 Emweb bvba, Herent, Belgium.
*
* See the LICENSE file for terms of use.
*/
/* Note: this is at the same time valid JavaScript and C++. */
WT_DECLARE_WT_MEMBER
(1, JavaScriptConstructor, "WImage",
function(APP, el, target) {
jQuery.data(el, 'obj', this);
var self = this;
var WT = APP.WT;
var areaCoordsJSON = null;
this.setAreaCoordsJSON = function(newAreaCoordsJSON) {
areaCoordsJSON = newAreaCoordsJSON;
this.updateAreas();
}
this.updateAreas = function() {
this.updateAreaCoords();
}
this.updateAreaCoords = function() {
var combinedTransformJS = target.combinedTransform;
if (combinedTransformJS === undefined || areaCoordsJSON === null)
return;
var combinedTransform = combinedTransformJS();
var mult = APP.WT.gfxUtils.transform_mult;
for (var c = 0; c < areaCoordsJSON.length; c++)
{
var coordEntry = areaCoordsJSON[c];
var areaEl = coordEntry[0];
var points = coordEntry[1];
var len = points.length;
var new_coords = "";
for (var i = 0; i + 1 < len; i += 2) {
if (i > 0)
new_coords += ",";
var p = mult(combinedTransform, points.slice(i, i + 2));
new_coords += Math.round(p[0]).toString() + "," + Math.round(p[1]).toString();
}
if (i < len) // a circle -- copy radius unchanged
new_coords += ("," + points[i].toString());
if (areaEl)
areaEl.coords = new_coords;
}
}
});
(1-1/5)