diff --git a/resources/celerity/map.php b/resources/celerity/map.php index ab2565ab77..f08162cb6d 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -81,7 +81,7 @@ return array( 'rsrc/css/application/phame/phame.css' => '19ecc703', 'rsrc/css/application/pholio/pholio-edit.css' => '3ad9d1ee', 'rsrc/css/application/pholio/pholio-inline-comments.css' => '95004a57', - 'rsrc/css/application/pholio/pholio.css' => '6f87390f', + 'rsrc/css/application/pholio/pholio.css' => 'd0502625', 'rsrc/css/application/phortune/phortune-credit-card-form.css' => 'b25b4beb', 'rsrc/css/application/phrequent/phrequent.css' => 'ffc185ad', 'rsrc/css/application/phriction/phriction-document-css.css' => '7d7f0071', @@ -131,6 +131,7 @@ return array( 'rsrc/css/phui/phui-form.css' => 'b78ec020', 'rsrc/css/phui/phui-header-view.css' => 'a2071a67', 'rsrc/css/phui/phui-icon.css' => 'd8526aa1', + 'rsrc/css/phui/phui-image-mask.css' => '5f4a6d5d', 'rsrc/css/phui/phui-info-panel.css' => '27ea50a1', 'rsrc/css/phui/phui-list.css' => '43ed2d93', 'rsrc/css/phui/phui-object-box.css' => 'ce92d8ec', @@ -230,6 +231,7 @@ return array( 'rsrc/image/credit_cards.png' => '72b8ede8', 'rsrc/image/darkload.gif' => '1ffd3ec6', 'rsrc/image/divot.png' => '94dded62', + 'rsrc/image/examples/hero.png' => '979a86ae', 'rsrc/image/grippy_texture.png' => 'aca81e2f', 'rsrc/image/icon/fatcow/arrow_branch.png' => '2537c01c', 'rsrc/image/icon/fatcow/arrow_merge.png' => '21b660e0', @@ -750,7 +752,7 @@ return array( 'phabricator-uiexample-reactor-sendproperties' => '551add57', 'phabricator-zindex-css' => 'efb673ac', 'phame-css' => '19ecc703', - 'pholio-css' => '6f87390f', + 'pholio-css' => 'd0502625', 'pholio-edit-css' => '3ad9d1ee', 'pholio-inline-comments-css' => '95004a57', 'phortune-credit-card-form' => '2290aeef', @@ -771,6 +773,7 @@ return array( 'phui-form-view-css' => 'ed856191', 'phui-header-view-css' => 'a2071a67', 'phui-icon-view-css' => 'd8526aa1', + 'phui-image-mask-css' => '5f4a6d5d', 'phui-info-panel-css' => '27ea50a1', 'phui-list-view-css' => '43ed2d93', 'phui-object-box-css' => 'ce92d8ec', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index cd8ff2e05c..346daefb0c 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1026,6 +1026,8 @@ phutil_register_library_map(array( 'PHUIHeaderView' => 'view/phui/PHUIHeaderView.php', 'PHUIIconExample' => 'applications/uiexample/examples/PHUIIconExample.php', 'PHUIIconView' => 'view/phui/PHUIIconView.php', + 'PHUIImageMaskExample' => 'applications/uiexample/examples/PHUIImageMaskExample.php', + 'PHUIImageMaskView' => 'view/phui/PHUIImageMaskView.php', 'PHUIInfoPanelExample' => 'applications/uiexample/examples/PHUIInfoPanelExample.php', 'PHUIInfoPanelView' => 'view/phui/PHUIInfoPanelView.php', 'PHUIListExample' => 'applications/uiexample/examples/PHUIListExample.php', @@ -3793,6 +3795,8 @@ phutil_register_library_map(array( 'PHUIHeaderView' => 'AphrontView', 'PHUIIconExample' => 'PhabricatorUIExample', 'PHUIIconView' => 'AphrontTagView', + 'PHUIImageMaskExample' => 'PhabricatorUIExample', + 'PHUIImageMaskView' => 'AphrontTagView', 'PHUIInfoPanelExample' => 'PhabricatorUIExample', 'PHUIInfoPanelView' => 'AphrontView', 'PHUIListExample' => 'PhabricatorUIExample', diff --git a/src/applications/pholio/controller/PholioMockViewController.php b/src/applications/pholio/controller/PholioMockViewController.php index 21032d70c6..6606dd3fe2 100644 --- a/src/applications/pholio/controller/PholioMockViewController.php +++ b/src/applications/pholio/controller/PholioMockViewController.php @@ -100,6 +100,7 @@ final class PholioMockViewController extends PholioController { $xaction_view = id(new PholioTransactionView()) ->setUser($this->getRequest()->getUser()) + ->setMock($mock) ->setObjectPHID($mock->getPHID()) ->setTransactions($xactions) ->setMarkupEngine($engine); diff --git a/src/applications/pholio/view/PholioTransactionView.php b/src/applications/pholio/view/PholioTransactionView.php index f51f3e3a2c..043417c9fa 100644 --- a/src/applications/pholio/view/PholioTransactionView.php +++ b/src/applications/pholio/view/PholioTransactionView.php @@ -6,6 +6,17 @@ final class PholioTransactionView extends PhabricatorApplicationTransactionView { + private $mock; + + public function setMock($mock) { + $this->mock = $mock; + return $this; + } + + public function getMock() { + return $this->mock; + } + protected function shouldGroupTransactions( PhabricatorApplicationTransaction $u, PhabricatorApplicationTransaction $v) { @@ -87,12 +98,31 @@ final class PholioTransactionView private function renderInlineContent(PholioTransaction $inline) { $comment = $inline->getComment(); + $mock = $this->getMock(); + $images = $mock->getAllImages(); + $images = mpull($images, null, 'getID'); - $thumb = phutil_tag( - 'img', - array( - 'src' => '/pholio/inline/thumb/'.$comment->getImageID(), - )); + $image = idx($images, $comment->getImageID()); + if (!$image) { + throw new Exception('No image attached!'); + } + + $file = $image->getFile(); + if (!$file->isViewableImage()) { + throw new Exception('File is not viewable.'); + } + + $image_uri = $file->getBestURI(); + + $thumb = id(new PHUIImageMaskView()) + ->addClass('mrl') + ->setImage($image_uri) + ->setDisplayHeight(100) + ->setDisplayWidth(200) + ->withMask(true) + ->centerViewOnPoint( + $comment->getX(), $comment->getY(), + $comment->getHeight(), $comment->getWidth()); $link = phutil_tag( 'a', diff --git a/src/applications/uiexample/examples/PHUIBoxExample.php b/src/applications/uiexample/examples/PHUIBoxExample.php index 0ef20229f2..8e03452de1 100644 --- a/src/applications/uiexample/examples/PHUIBoxExample.php +++ b/src/applications/uiexample/examples/PHUIBoxExample.php @@ -114,5 +114,5 @@ final class PHUIBoxExample extends PhabricatorUIExample { $head4, $obj4, )); - } + } } diff --git a/src/applications/uiexample/examples/PHUIImageMaskExample.php b/src/applications/uiexample/examples/PHUIImageMaskExample.php new file mode 100644 index 0000000000..d126dd6789 --- /dev/null +++ b/src/applications/uiexample/examples/PHUIImageMaskExample.php @@ -0,0 +1,88 @@ +addClass('ml') + ->setImage($image) + ->setDisplayHeight($display_height) + ->setDisplayWidth($display_width) + ->centerViewOnPoint(265, 185, 30, 140); + + $mask2 = id(new PHUIImageMaskView()) + ->addClass('ml') + ->setImage($image) + ->setDisplayHeight($display_height) + ->setDisplayWidth($display_width) + ->centerViewOnPoint(18, 18, 40, 80); + + $mask3 = id(new PHUIImageMaskView()) + ->addClass('ml') + ->setImage($image) + ->setDisplayHeight($display_height) + ->setDisplayWidth($display_width) + ->centerViewOnPoint(265, 185, 30, 140) + ->withMask(true); + + $mask4 = id(new PHUIImageMaskView()) + ->addClass('ml') + ->setImage($image) + ->setDisplayHeight($display_height) + ->setDisplayWidth($display_width) + ->centerViewOnPoint(18, 18, 40, 80) + ->withMask(true); + + $mask5 = id(new PHUIImageMaskView()) + ->addClass('ml') + ->setImage($image) + ->setDisplayHeight($display_height) + ->setDisplayWidth($display_width) + ->centerViewOnPoint(254, 272, 60, 240) + ->withMask(true); + + $box1 = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Center is in the middle')) + ->appendChild($mask1); + + $box2 = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Center is on an edge')) + ->appendChild($mask2); + + $box3 = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Center Masked')) + ->appendChild($mask3); + + $box4 = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Edge Masked')) + ->appendChild($mask4); + + $box5 = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Wide Masked')) + ->appendChild($mask5); + + return phutil_tag( + 'div', + array(), + array( + $box1, + $box2, + $box3, + $box4, + $box5 + )); + } +} diff --git a/src/view/phui/PHUIImageMaskView.php b/src/view/phui/PHUIImageMaskView.php new file mode 100644 index 0000000000..44233db93c --- /dev/null +++ b/src/view/phui/PHUIImageMaskView.php @@ -0,0 +1,116 @@ +image = $image; + return $this; + } + + public function setDisplayWidth($width) { + $this->displayWidth = $width; + return $this; + } + + public function setDisplayHeight($height) { + $this->displayHeight = $height; + return $this; + } + + public function centerViewOnPoint($x, $y, $h, $w) { + $this->centerX = $x; + $this->centerY = $y; + $this->maskH = $h; + $this->maskW = $w; + return $this; + } + + public function withMask($mask) { + $this->withMask = $mask; + return $this; + } + + public function getTagName() { + return 'div'; + } + + public function getTagAttributes() { + require_celerity_resource('phui-image-mask-css'); + + $classes = array(); + $classes[] = 'phui-image-mask'; + + $styles = array(); + $styles[] = 'height: '.$this->displayHeight.'px;'; + $styles[] = 'width: '.$this->displayWidth.'px;'; + + return array( + 'class' => implode(' ', $classes), + 'styles' => implode(' ', $styles), + ); + + } + + public function getTagContent() { + + /* Center it in the middle of the selected area */ + $center_x = round($this->centerX + ($this->maskW / 2)); + $center_y = round($this->centerY + ($this->maskH / 2)); + $center_x = round($center_x - ($this->displayWidth / 2)); + $center_y = round($center_y - ($this->displayHeight / 2)); + + $center_x = -$center_x; + $center_y = -$center_y; + + $classes = array(); + $classes[] = 'phui-image-mask-image'; + + $styles = array(); + $styles[] = 'height: '.$this->displayHeight.'px;'; + $styles[] = 'width: '.$this->displayWidth.'px;'; + $styles[] = 'background-image: url('.$this->image.');'; + $styles[] = 'background-position: '.$center_x.'px '.$center_y.'px;'; + + $mask = null; + if ($this->withMask) { + /* The mask is a 300px border around a transparent box. + so we do the math here to position the box correctly. */ + $border = 300; + $left = round((($this->displayWidth - $this->maskW) / 2) - $border); + $top = round((($this->displayHeight - $this->maskH) / 2) - $border); + + $mstyles = array(); + $mstyles[] = 'left: '.$left.'px;'; + $mstyles[] = 'top: '.$top.'px;'; + $mstyles[] = 'height: '.$this->maskH.'px;'; + $mstyles[] = 'width: '.$this->maskW.'px;'; + + $mask = phutil_tag( + 'span', + array( + 'class' => 'phui-image-mask-mask', + 'style' => implode(' ', $mstyles), + ), + null); + } + + return phutil_tag( + 'div', + array( + 'class' => implode(' ', $classes), + 'style' => implode(' ', $styles), + ), + $mask); + } +} diff --git a/webroot/rsrc/css/application/pholio/pholio.css b/webroot/rsrc/css/application/pholio/pholio.css index f1e21a2978..38fdcd3313 100644 --- a/webroot/rsrc/css/application/pholio/pholio.css +++ b/webroot/rsrc/css/application/pholio/pholio.css @@ -99,19 +99,21 @@ .pholio-transaction-inline-comment .transaction-comment { display: table-cell; - vertical-align: middle; + vertical-align: top; padding-left: 8px; + padding-top: 4px; } .pholio-mock-reticle { position: absolute; display: none; box-sizing: border-box; + border: 4px solid transparent; } .pholio-mock-reticle-selection { - background-color: rgba(255, 255, 255, 0.50); - border: 1px dashed #000; + border: 1px solid rgba(0,0,0,.5); + box-shadow: 0 0 0 4px rgba(255,255,255,.5); } .pholio-mock-reticle-draft { @@ -130,8 +132,12 @@ .pholio-mock-reticle-draft:hover, .pholio-mock-reticle-final:hover { - background-color: rgba(255, 255, 255, 0.50); + border: 1px solid rgba(0,0,0,.5); + box-shadow: 0 0 0 4px rgba(255,255,255,.5); cursor: pointer; + color: transparent; + text-shadow: none; + -webkit-text-stroke: 0; } .device-desktop .mock-has-cursor .pholio-mock-reticle { diff --git a/webroot/rsrc/css/phui/phui-image-mask.css b/webroot/rsrc/css/phui/phui-image-mask.css new file mode 100644 index 0000000000..992d5e627a --- /dev/null +++ b/webroot/rsrc/css/phui/phui-image-mask.css @@ -0,0 +1,23 @@ +/** + * @provides phui-image-mask-css + */ + + +.phui-image-mask { + background: url('/rsrc/image/checker_lighter.png'); + display: inline-block; + border: 1px solid {$lightblueborder}; + padding: 4px; + position: relative; + overflow: hidden; +} + +.phui-image-mask-image { + background-repeat: no-repeat; +} + +.phui-image-mask-mask { + border: 300px solid rgba(0, 0, 0, 0.5); + background-clip: content-box; + position: absolute; +} diff --git a/webroot/rsrc/image/examples/hero.png b/webroot/rsrc/image/examples/hero.png new file mode 100644 index 0000000000..2e13205659 Binary files /dev/null and b/webroot/rsrc/image/examples/hero.png differ