Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Show pixel info under the mouse when previewing image. #5944

Merged
merged 20 commits into from
Nov 11, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b35c9e8
Implement cursor info tip showing image coordinates under the cursor.
RaymondLim Nov 6, 2013
b5602d6
Merge changes in master and resolve conflict in brackets.less.
RaymondLim Nov 6, 2013
75d10da
Switch to custom cursor
RaymondLim Nov 6, 2013
42a11cf
Adding cursor images from Larz.
RaymondLim Nov 6, 2013
2c479a9
Remove the code that I accidently reintroduced when resolving the mer…
RaymondLim Nov 6, 2013
b371230
Prevent a possible divide-by-zero error and a minor coordinate alignm…
RaymondLim Nov 6, 2013
f08078b
Merge remote-tracking branch 'origin' into rlim/cursor-info-on-image
RaymondLim Nov 6, 2013
3a38e61
New cursor images and minor tweak to cursor-info padding
larz0 Nov 7, 2013
e12d708
Temporarily hide scale sticker to show cursor info behind it.
RaymondLim Nov 8, 2013
6bc2023
Use table to format the x and y coordinate values as suggested by Larz.
RaymondLim Nov 9, 2013
20b2a82
Initialize _scale to 100 for every image load.
RaymondLim Nov 9, 2013
d58c351
Moving the initialization to the top of render function.
RaymondLim Nov 9, 2013
bac09cc
Merge remote-tracking branch 'origin' into rlim/cursor-info-on-image
RaymondLim Nov 9, 2013
0e6dd73
Merge remote-tracking branch 'origin' into rlim/cursor-info-on-image
RaymondLim Nov 9, 2013
627230e
Use cross guides instead of crosshair cursor.
RaymondLim Nov 11, 2013
210739a
Fix some issues with mouse entering/exiting image scale sticker.
RaymondLim Nov 11, 2013
160cedc
Adjust the color of guides with alpha value.
RaymondLim Nov 11, 2013
0ca7e55
Remove the custom cursor files no longer needed.
RaymondLim Nov 11, 2013
653890b
Remove the unused css rule.
RaymondLim Nov 11, 2013
925997a
Made some changes for review feedback.
RaymondLim Nov 11, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 228 additions & 7 deletions src/editor/ImageViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@


/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, Mustache */
/*global define, $, window, Mustache */
define(function (require, exports, module) {
"use strict";

Expand All @@ -36,15 +36,19 @@ define(function (require, exports, module) {
StringUtils = require("utils/StringUtils"),
FileSystem = require("filesystem/FileSystem");

var _naturalWidth = 0;
var _naturalWidth = 0,
_scale = 100,
_scaleDivInfo = null; // coordinates of hidden scale sticker

/** Update the scale element, i.e. on resize
* @param {!string} currentWidth actual width of image in view
*/
function _updateScale(currentWidth) {
if (currentWidth < _naturalWidth) {
var scale = Math.floor(currentWidth / _naturalWidth * 100);
$("#img-scale").text(scale + "%")
_scale = currentWidth / _naturalWidth * 100;
$("#img-scale").text(Math.floor(_scale) + "%")
// Keep the position of the image scale div relative to the image.
.css("left", $("#img-preview").position().left + 5)
.show();
} else {
$("#img-scale").hide();
Expand All @@ -70,6 +74,200 @@ define(function (require, exports, module) {
}
}

/**
* Check mouse entering/exiting the scale sticker.
* Hide it when entering and show it again when exiting.
*
* @param {number} offsetX mouse offset from the left of the previewing image
* @param {number} offsetY mouseoffset from the top of the previewing image
*/
function _handleMouseEnterOrExitScaleSticker(offsetX, offsetY) {
var imagePos = $("#img-preview").position(),
scaleDivPos = $("#img-scale").position(),
imgWidth = $("#img-preview").width(),
imgHeight = $("#img-preview").height(),
scaleDivLeft,
scaleDivTop,
scaleDivRight,
scaleDivBottom;

if (_scaleDivInfo) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should reset the state of this var in render(), as done for _scale. When opening 2 scaled images consecutivley then the mouse can move over the scale and it will not be hidden immediately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Fixed.

scaleDivLeft = _scaleDivInfo.left;
scaleDivTop = _scaleDivInfo.top;
scaleDivRight = _scaleDivInfo.right;
scaleDivBottom = _scaleDivInfo.bottom;

if ((imgWidth + imagePos.left) < scaleDivRight) {
scaleDivRight = imgWidth + imagePos.left;
}

if ((imgHeight + imagePos.top) < scaleDivBottom) {
scaleDivBottom = imgHeight + imagePos.top;
}

} else {
scaleDivLeft = scaleDivPos.left;
scaleDivTop = scaleDivPos.top;
scaleDivRight = $("#img-scale").width() + scaleDivLeft;
scaleDivBottom = $("#img-scale").height() + scaleDivTop;
}

if (_scaleDivInfo) {
// See whether the cursor is no longer inside the hidden scale div.
// If so, show it again.
if ((offsetX < scaleDivLeft || offsetX > scaleDivRight) ||
(offsetY < scaleDivTop || offsetY > scaleDivBottom)) {
_scaleDivInfo = null;
$("#img-scale").show();
}
} else if ((offsetX >= scaleDivLeft && offsetX <= scaleDivRight) &&
(offsetY >= scaleDivTop && offsetY <= scaleDivBottom)) {
// Handle mouse inside image scale div.
// But hide it only if the pixel under mouse is also in the image.
if (offsetX < (imagePos.left + imgWidth) &&
offsetY < (imagePos.top + imgHeight)) {
// Remember image scale div coordinates before hiding it.
_scaleDivInfo = {left: scaleDivPos.left,
top: scaleDivPos.top,
right: scaleDivRight,
bottom: scaleDivBottom};
$("#img-scale").hide();
}
}
}

/**
* Show image coordinates under the mouse cursor
*
* @param {MouseEvent} e mouse move event
*/
function _showImageTip(e) {
// Don't show image tip if _scale is close to zero.
// since we won't have enough room to show tip anyway.
if (Math.floor(_scale) === 0) {
return;
}

var x = Math.floor(e.offsetX * 100 / _scale),
y = Math.floor(e.offsetY * 100 / _scale),
$target = $(e.target),
targetPos = $target.position(),
tipPos = $("#img-tip").position(),
imagePos = $("#img-preview").position(),
scaleDivPos = $("#img-scale").position(),
left = e.offsetX + imagePos.left,
top = e.offsetY + imagePos.top,
width = $("#img-preview").width(),
height = $("#img-preview").height(),
windowWidth = $(window).width(),
fourDigitImageWidth = _naturalWidth.toString().length === 4,
infoWidth1 = 112, // info div width 96px + vertical toolbar width 16px
infoWidth2 = 120, // info div width 104px (for 4-digit image width) + vertical toolbar width 16px
tipOffsetX = 10, // adjustment for info div left from x coordinate of cursor
tipOffsetY = -54, // adjustment for info div top from y coordinate of cursor
tipMinusOffsetX1 = -82, // for less than 4-digit image width
tipMinusOffsetX2 = -90; // for 4-digit image width

// Adjust left, top, x and y based on which element contains the cursor.
// Return if the target element is no longer available as in the case of
// a vertical guide that has its left equals to zero.
if ($target.is(".img-guide")) {
if ($target.is("#vert-guide")) {
if (targetPos.left === 0) {
return;
}
left = targetPos.left;
x = Math.floor(left * 100 / _scale);
} else {
if (targetPos.top === 0) {
return;
}
top = targetPos.top;
y = Math.floor(top * 100 / _scale);
}
} else if (!$target.is("#img-preview")) {
if ($target.is("#img-scale")) {
left = scaleDivPos.left + e.offsetX;
top = scaleDivPos.top + e.offsetY;
x = Math.floor(left * 100 / _scale);
y = Math.floor(top * 100 / _scale);
} else if (tipPos.left && tipPos.top) {
// Cursor must be inside the image tip.
left = tipPos.left + e.offsetX;
top = tipPos.top + e.offsetY;
x = Math.floor(left * 100 / _scale);
y = Math.floor(top * 100 / _scale);
} else {
return;
}
}

_handleMouseEnterOrExitScaleSticker(left, top);
if ($(e.target).is("#img-scale")) {
// If we're in the scale sticker, then just return.
return;
}

// Check whether to show the image tip on the left.
if ((e.pageX + infoWidth1) > windowWidth ||
(fourDigitImageWidth && (e.pageX + infoWidth2) > windowWidth)) {
tipOffsetX = fourDigitImageWidth ? tipMinusOffsetX2 : tipMinusOffsetX1;
}

// For some reason we're getting -1 for e.offset when hovering over the very
// first pixel of a scaled image. So adjust x to 0 if it is negative.
if (x < 0) {
x = 0;
}

$("#x-value").text(x + "px");
$("#y-value").text(y + "px");

$("#img-tip").css({
left: left + tipOffsetX,
top: top + tipOffsetY
}).show();

$("#horiz-guide").css({
left: imagePos.left,
top: top,
width: width - 1
}).show();

$("#vert-guide").css({
left: left,
top: imagePos.top,
height: height - 1
}).show();
}

/**
* Show image coordinates under the mouse cursor
*
* @param {MouseEvent} e mouse leave event
*/
function _hideImageTip(e) {
var $target = $(e.target),
targetPos = $target.position(),
imagePos = $("#img-preview").position(),
right = imagePos.left + $("#img-preview").width(),
bottom = imagePos.top + $("#img-preview").height(),
offsetX = e.offsetX,
offsetY = e.offsetY;

if ($target.is(".img-guide")) {
offsetX = targetPos.left + offsetX;
offsetY = targetPos.top + offsetY;
}

// Hide image tip and guides only if the cursor is outside of the image.
if (offsetX < imagePos.left || offsetX >= right ||
offsetY < imagePos.top || offsetY >= bottom) {
$("#img-tip").hide();
$(".img-guide").hide();
}
}

/**
* creates a DOM node to place in the editor-holder
* in order to display an image.
Expand All @@ -87,15 +285,21 @@ define(function (require, exports, module) {
function _removeListeners() {
$(PanelManager).off("editorAreaResize", _onEditorAreaResize);
$(DocumentManager).off("fileNameChange", _onFileNameChange);
$("#img").off("mousemove", "#img-preview, #img-scale, #img-tip, .img-guide", _showImageTip)
.off("mouseleave", "#img-preview, #img-scale, #img-tip, .img-guide", _hideImageTip);
}

/** Perform decorations on the view that require loading the image in the browser,
* i.e. getting actual and natural width and height andplacing the scale sticker
* @param {!string} fullPath path to the image file
/**
* Perform decorations on the view that require loading the image in the browser,
* i.e. getting actual and natural width and height andplacing the scale sticker
* @param {!string} fullPath path to the image file
*/
function render(fullPath) {
var relPath = ProjectManager.makeProjectRelativeIfPossible(fullPath);

_scale = 100; // initialize to 100
_scaleDivInfo = null;

$("#img-path").text(relPath)
.attr("title", relPath);
$("#img-preview").on("load", function () {
Expand All @@ -104,6 +308,7 @@ define(function (require, exports, module) {
var dimensionString = _naturalWidth + " &times; " + this.naturalHeight + " " + Strings.UNIT_PIXELS;
// get image size
var file = FileSystem.getFileForPath(fullPath);
var minimumPixels = 20; // for showing crosshair cursor
file.stat(function (err, stat) {
if (err) {
$("#img-data").html(dimensionString);
Expand All @@ -125,9 +330,25 @@ define(function (require, exports, module) {
// listen to removal to stop listening to resize events
$(EditorManager).on("removeCustomViewer", _removeListeners);
$(DocumentManager).on("fileNameChange", _onFileNameChange);

$("#img-tip").hide();
$(".img-guide").hide();
$("#img").on("mousemove", "#img-preview, #img-scale, #img-tip, .img-guide", _showImageTip)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like to see all events handled in a single function. That's fenitely an improvement that makes this code more maintainable.

.on("mouseleave", "#img-preview, #img-scale, #img-tip, .img-guide", _hideImageTip);

_updateScale($(this).width());
minimumPixels = Math.floor(minimumPixels * 100 / _scale);

// If the image size is too narrow in width or height, then
// show the crosshair cursor since guides are almost invisible
// in narrow images.
if (this.naturalWidth < minimumPixels || this.naturalHeight < minimumPixels) {
$("#img-preview").css("cursor", "crosshair");
$(".img-guide").css("cursor", "crosshair");
}
});
}

exports.getCustomViewHolder = getCustomViewHolder;
exports.render = render;
});
14 changes: 14 additions & 0 deletions src/htmlContent/image-holder.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@
<div id="img">
<div id="img-scale"></div>
<img id="img-preview" src="file:///{{fullPath}}">
<div id="img-tip">
<table class="tip-container">
<tr>
<td class="variable">x: </td>
<td id="x-value"></td>
</tr>
<tr>
<td class="variable">y: </td>
<td id="y-value"></td>
</tr>
</table>
</div>
<div id="horiz-guide" class="img-guide"></div>
<div id="vert-guide" class="img-guide"></div>
</div>
</div>
</div>
57 changes: 51 additions & 6 deletions src/styles/brackets.less
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ a, img {
.box-align(center);
background: @background-color-3 url('images/no_content_bg.svg') no-repeat center 45%;
}

/* Image Preview */
#image-holder {
overflow: hidden;
Expand Down Expand Up @@ -291,17 +292,17 @@ a, img {
#img-path::selection {
background: @selection-color-focused;
}

#img {
position: relative;

/* These properties keep the img percentage sticky */
display: inline-block;
min-width: 1px;
}

.img-guide,
#img-preview {
cursor: none;
}

#img-scale {
display: none;
display: block;
position: absolute;
top: 5px;
Expand All @@ -313,6 +314,50 @@ a, img {
border-radius: 3px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

#img-tip {
display: block;
position: absolute;
text-align: left;
white-space: nowrap;
padding: 6px 9px;
color: #fff;
background-color: rgba(0, 0, 0, 0.8);
font-size: 11px;
font-family: SourceCodePro;
line-height: 13px;
border-radius: 3px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24);
}

#x-value,
#y-value {
text-align: right;
}

.tip-container {
border: 0;
}

#horiz-guide {
background-image: url("images/horizontal-dash.svg");
background-repeat: repeat-x;
width: 8px;
height: 1px;
}

#vert-guide {
background-image: url("images/vertical-dash.svg");
background-repeat: repeat-y;
width: 1px;
height: 8px;
}

#horiz-guide,
#vert-guide {
position: absolute;
display: block;
}
}

.vert-resizer {
Expand Down
Loading