8 Commits

Author SHA1 Message Date
Sapozhnik Ivan e1950e0352 Merge pull request #17 from iSapozhnik/feature/hover-insets
Appearance
2020-05-14 19:23:45 +02:00
Ivan Sapozhnik 622cad7feb Appearance 2020-05-14 19:17:48 +02:00
Sapozhnik Ivan 971fd9d77f Merge pull request #14 from iSapozhnik/feature/hover-insets
Hover insets
2020-04-22 22:47:08 +02:00
Ivan Sapozhnik c71aaecd87 Hover edge insets 2020-04-22 22:44:53 +02:00
Sapozhnik Ivan e6801e978a Merge pull request #11 from iSapozhnik/feature/hover-corner-radius
Hover corner radius
2020-04-22 22:14:27 +02:00
Ivan Sapozhnik 83e292f59c Readme 2020-04-22 22:03:54 +02:00
Ivan Sapozhnik abd8f1a338 Hover corner radius 2020-04-22 22:02:00 +02:00
Ivan Sapozhnik 63ea1f9b30 Merge branch 'master' into development 2020-04-22 21:56:07 +02:00
10 changed files with 74 additions and 61 deletions
+7 -8
View File
@@ -5,7 +5,6 @@
![Swift](https://img.shields.io/badge/%20in-swift%205.0-orange.svg)
![macOS](https://img.shields.io/badge/macOS-10.12-green.svg)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FiSapozhnik%2FMenu%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/iSapozhnik/Menu)
</div>
@@ -64,6 +63,8 @@ public protocol Configuration {
var menuItemHoverBackgroundColor: NSColor { get }
var menuItemTextColor: NSColor { get }
var menuItemHoverTextColor: NSColor { get }
var menuItemHoverCornerRadius: CGFloat { get }
var menuItemHoverEdgeInsets: NSEdgeInsets { get }
var menuItemCheckmarkColor: NSColor { get }
var menuItemHoverCheckmarkColor: NSColor { get }
var menuItemCheckmarkHeight: CGFloat { get }
@@ -122,7 +123,7 @@ class ViewController: NSViewController {
myMenu.addItems(menuItems)
}
@IBAction func didClickButton(_ sender: NSButton) {
@IBAction func didClickedButton(_ sender: NSButton) {
myMenu.show(from: sender)
}
}
@@ -130,14 +131,12 @@ class ViewController: NSViewController {
## Examples
In this section I've collected some examples of what can be i,plemented do using **Menu** control. On the left side some random example from Dribbble and on the right side my implementation.
In this section I've collected some examples of what can be implemented by using **Menu** control. On the left side some random examples from Dribbble and on the right side there is my implementation.
| Dribbble | Menu | Code |
| ------------- |:-------------:|:-------------:|
| Dribbble | Menu |
| ------------- |:-------------:|
| [link](https://dribbble.com/shots/4233782-Snooze-notifications-in-Twist) | |
| ![](examples/twist.png) | ![](examples/menu_twist.png) | |
| [link](https://dribbble.com/shots/7055473-Dropdowns) | | |
| ![](examples/example_0.png) | ![](examples/menu_0.png) | [code](examples/examples.md) |
| ![](examples/twist.png) | ![](examples/example_twist.png) |
## Credits
+7 -9
View File
@@ -11,14 +11,11 @@ class Control: NSControl {
var hover: ((Bool) -> Void)?
private let hoverLayer = CAShapeLayer()
private let hoverColor: NSColor
private let hoverAnimationDuration: TimeInterval
private var trackingArea: NSTrackingArea?
private let configuration: Configuration
init(with configuration: Configuration) {
self.hoverColor = configuration.menuItemHoverBackgroundColor
self.hoverAnimationDuration = configuration.menuItemHoverAnimationDuration
self.configuration = configuration
super.init(frame: .zero)
wantsLayer = true
@@ -33,7 +30,8 @@ class Control: NSControl {
override func layout() {
super.layout()
hoverLayer.path = CGPath(rect: bounds, transform: nil)
let newRect = bounds.inset(by: configuration.menuItemHoverEdgeInsets)
hoverLayer.path = CGPath(roundedRect: newRect, cornerWidth: configuration.menuItemHoverCornerRadius, cornerHeight: configuration.menuItemHoverCornerRadius, transform: nil)
if let trackingArea = trackingArea, trackingAreas.contains(trackingArea) {
removeTrackingArea(trackingArea)
@@ -61,13 +59,13 @@ class Control: NSControl {
override func mouseEntered(with event: NSEvent) {
guard isEnabled else { return }
animateFillColor(from: .clear, to: hoverColor)
animateFillColor(from: .clear, to: configuration.menuItemHoverBackgroundColor)
hover?(true)
}
override func mouseExited(with event: NSEvent) {
guard isEnabled else { return }
animateFillColor(from: hoverColor, to: .clear)
animateFillColor(from: configuration.menuItemHoverBackgroundColor, to: .clear)
hover?(false)
}
@@ -78,7 +76,7 @@ class Control: NSControl {
private func animateFillColor(from oldColor: NSColor, to newColor: NSColor) {
let animation = CABasicAnimation(keyPath: "fillColor")
animation.duration = hoverAnimationDuration
animation.duration = configuration.menuItemHoverAnimationDuration
animation.fromValue = oldColor.cgColor
animation.toValue = newColor.cgColor
animation.fillMode = .both
+11
View File
@@ -190,3 +190,14 @@ extension CGFloat {
/// 48 points
static let grid6: CGFloat = 48.0
}
extension NSRect {
func inset(by insets: NSEdgeInsets) -> NSRect {
var newRect = self
newRect.origin.x += insets.left
newRect.size.width -= insets.left + insets.right
newRect.origin.y += insets.top
newRect.size.height -= insets.top + insets.bottom
return newRect
}
}
+21 -2
View File
@@ -65,6 +65,14 @@ public final class Menu {
item.action?()
}
public func selectItem(withTitle title: String) {
guard let item = items.first(where: { item -> Bool in
item.title == title
}) else { return }
selectedId = item.id
item.action?()
}
// MARK: - Adding and Removing Menu Items
public func insertItem(_ item: MenuItem, at index: Int) {
items.insert(item, at: index)
@@ -218,12 +226,23 @@ public final class Menu {
guard let parentWindow = view.window, let topMostSuperView = parentWindow.contentView else { return }
let locationInWindow = view.convert(topMostSuperView.frame.origin, to: nil)
let rectInWindow = NSRect(origin: locationInWindow, size: CGSize(width: NSWidth(view.frame), height: NSHeight(window.frame)))
let rectInWindow = NSRect(origin: locationInWindow, size: CGSize(width: NSWidth(view.frame), height: NSHeight(view.frame)))
let rectInScreen = parentWindow.convertToScreen(rectInWindow)
let origin = rectInScreen.origin
var additionalYOffset = configuration.appearsBelowSender ? NSHeight(view.frame) : 0
additionalYOffset += abs(configuration.presentingOffset)
let newFrame = NSRect(x: origin.x, y: origin.y - NSHeight(window.frame) - additionalYOffset, width: NSWidth(view.frame), height: NSHeight(window.frame))
let newFrame: NSRect
switch configuration.appearancePosition {
case .rightBottom:
newFrame = NSRect(x: origin.x, y: origin.y - NSHeight(window.frame) - additionalYOffset, width: NSWidth(view.frame), height: NSHeight(window.frame))
case .rightTop:
newFrame = NSRect(x: origin.x, y: origin.y + additionalYOffset, width: NSWidth(view.frame), height: NSHeight(window.frame))
case .leftTop:
newFrame = NSRect(x: origin.x - NSWidth(window.frame) + NSWidth(view.frame), y: origin.y - additionalYOffset, width: NSWidth(view.frame), height: NSHeight(window.frame))
case .leftBottom:
newFrame = NSRect(x: origin.x - NSWidth(window.frame) + NSWidth(view.frame), y: origin.y - NSHeight(window.frame) - additionalYOffset, width: NSWidth(view.frame), height: NSHeight(window.frame))
}
window.setFrame(newFrame, display: true, animate: false)
}
+22
View File
@@ -12,6 +12,13 @@ public enum Alignment {
case right
}
public enum Position {
case leftBottom
case leftTop
case rightTop
case rightBottom
}
public enum Padding {
public struct Horizontal {
let left: CGFloat
@@ -54,6 +61,7 @@ public protocol Configuration {
var cornerRadius: CGFloat { get }
var hasShadow: Bool { get }
var appearsBelowSender: Bool { get }
var appearancePosition: Position { get }
var presentingOffset: CGFloat { get }
var animationDuration: TimeInterval { get }
var contentEdgeInsets: NSEdgeInsets { get }
@@ -70,6 +78,8 @@ public protocol Configuration {
var menuItemHoverBackgroundColor: NSColor { get }
var menuItemTextColor: NSColor { get }
var menuItemHoverTextColor: NSColor { get }
var menuItemHoverCornerRadius: CGFloat { get }
var menuItemHoverEdgeInsets: NSEdgeInsets { get }
var menuItemCheckmarkColor: NSColor { get }
var menuItemHoverCheckmarkColor: NSColor { get }
var menuItemCheckmarkHeight: CGFloat { get }
@@ -112,6 +122,10 @@ open class MenuConfiguration: Configuration {
return true
}
open var appearancePosition: Position {
return .rightBottom
}
open var presentingOffset: CGFloat {
return 0.0
}
@@ -172,6 +186,14 @@ open class MenuConfiguration: Configuration {
return .white
}
open var menuItemHoverEdgeInsets: NSEdgeInsets {
return NSEdgeInsets.zero
}
open var menuItemHoverCornerRadius: CGFloat {
return 0.0
}
open var menuItemTextColor: NSColor {
return .white
}
+6 -4
View File
@@ -208,10 +208,12 @@ class MenuElement: NSView {
control.isEnabled = isEnabled
control.hover = { [weak self] isHover in
guard let self = self else { return }
label.textColor = isHover ? self.configuration.menuItemHoverTextColor : isSelected ? self.configuration.menuItemHoverImageTintColor : self.configuration.menuItemTextColor
if #available(OSX 10.14, *) {
leftImageView?.contentTintColor = isHover ? self.configuration.menuItemHoverImageTintColor : isSelected ? self.configuration.menuItemHoverImageTintColor : self.configuration.menuItemImageTintColor
rightImageView?.contentTintColor = isHover ? self.configuration.menuItemHoverImageTintColor : isSelected ? self.configuration.menuItemHoverImageTintColor : self.configuration.menuItemImageTintColor
if self.configuration.rememberSelection {
label.textColor = isHover ? self.configuration.menuItemHoverTextColor : isSelected ? self.configuration.menuItemHoverImageTintColor : self.configuration.menuItemTextColor
if #available(OSX 10.14, *) {
leftImageView?.contentTintColor = isHover ? self.configuration.menuItemHoverImageTintColor : isSelected ? self.configuration.menuItemHoverImageTintColor : self.configuration.menuItemImageTintColor
rightImageView?.contentTintColor = isHover ? self.configuration.menuItemHoverImageTintColor : isSelected ? self.configuration.menuItemHoverImageTintColor : self.configuration.menuItemImageTintColor
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

-38
View File
@@ -1,38 +0,0 @@
## Example
![](menu_0.png)
### Code:
```
class Config: MenuConfiguration {
override var cornerRadius: CGFloat {
return 15.0
}
override var backgroundColor: NSColor {
return NSColor(red: 63/255, green: 59/255, blue: 59/255, alpha: 1.0)
}
override var menuItemHoverBackgroundColor: NSColor {
return NSColor(red: 86/255, green: 81/255, blue: 81/255, alpha: 1.0)
}
override var menuItemHoverCornerRadius: CGFloat {
return 10.0
}
override var contentEdgeInsets: NSEdgeInsets {
return NSEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
}
override var menuItemHeight: CGFloat {
return 40.0
}
override var menuItemHoverEdgeInsets: NSEdgeInsets {
return NSEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)
}
}
```
Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB