mirror of
https://github.com/qvacua/vimr.git
synced 2024-10-27 10:23:12 +03:00
Draw bold/italic/underline and correct color
This commit is contained in:
parent
8e9c532b40
commit
c95d2c3201
@ -41,15 +41,15 @@ typedef struct {
|
|||||||
// FIXME: dunno whether we need this: copied from tui.c
|
// FIXME: dunno whether we need this: copied from tui.c
|
||||||
bool cont_received;
|
bool cont_received;
|
||||||
SignalWatcher cont_handle;
|
SignalWatcher cont_handle;
|
||||||
} OsxXpcUiData;
|
} XpcUiData;
|
||||||
|
|
||||||
static void sigcont_cb(SignalWatcher *watcher __unused, int signum __unused, void *data) {
|
static void sigcont_cb(SignalWatcher *watcher __unused, int signum __unused, void *data) {
|
||||||
((OsxXpcUiData *) data)->cont_received = true;
|
((XpcUiData *) data)->cont_received = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void osx_xpc_ui_scheduler(Event event, void *d) {
|
static void osx_xpc_ui_scheduler(Event event, void *d) {
|
||||||
UI *ui = d;
|
UI *ui = d;
|
||||||
OsxXpcUiData *data = ui->data;
|
XpcUiData *data = ui->data;
|
||||||
loop_schedule(data->loop, event);
|
loop_schedule(data->loop, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ static void osx_xpc_ui_main(UIBridgeData *bridge, UI *ui) {
|
|||||||
Loop loop;
|
Loop loop;
|
||||||
loop_init(&loop, NULL);
|
loop_init(&loop, NULL);
|
||||||
|
|
||||||
OsxXpcUiData *data = xcalloc(1, sizeof(OsxXpcUiData));
|
XpcUiData *data = xcalloc(1, sizeof(XpcUiData));
|
||||||
ui->data = data;
|
ui->data = data;
|
||||||
data->bridge = bridge;
|
data->bridge = bridge;
|
||||||
data->loop = &loop;
|
data->loop = &loop;
|
||||||
@ -91,7 +91,7 @@ static void osx_xpc_ui_main(UIBridgeData *bridge, UI *ui) {
|
|||||||
// FIXME: dunno whether we need this: copied from tui.c
|
// FIXME: dunno whether we need this: copied from tui.c
|
||||||
static void suspend_event(void **argv) {
|
static void suspend_event(void **argv) {
|
||||||
UI *ui = argv[0];
|
UI *ui = argv[0];
|
||||||
OsxXpcUiData *data = ui->data;
|
XpcUiData *data = ui->data;
|
||||||
data->cont_received = false;
|
data->cont_received = false;
|
||||||
|
|
||||||
kill(0, SIGTSTP);
|
kill(0, SIGTSTP);
|
||||||
@ -166,7 +166,30 @@ static void xpc_ui_scroll(UI *ui __unused, int count) {
|
|||||||
|
|
||||||
static void xpc_ui_highlight_set(UI *ui __unused, HlAttrs attrs) {
|
static void xpc_ui_highlight_set(UI *ui __unused, HlAttrs attrs) {
|
||||||
//printf("highlight set\n");
|
//printf("highlight set\n");
|
||||||
[neo_vim_osx_ui highlightSet:*((HighlightAttributes *)(&attrs))];
|
FontTrait trait = FontTraitNone;
|
||||||
|
if (attrs.italic) {
|
||||||
|
trait |= FontTraitItalic;
|
||||||
|
}
|
||||||
|
if (attrs.bold) {
|
||||||
|
trait |= FontTraitBold;
|
||||||
|
}
|
||||||
|
if (attrs.underline) {
|
||||||
|
trait |= FontTraitUnderline;
|
||||||
|
}
|
||||||
|
if (attrs.undercurl) {
|
||||||
|
trait |= FontTraitUndercurl;
|
||||||
|
}
|
||||||
|
CellAttributes cellAttrs;
|
||||||
|
cellAttrs.fontTrait = trait;
|
||||||
|
|
||||||
|
unsigned int fg = attrs.foreground == -1 ? qDefaultForeground : *((unsigned int*)(&attrs.foreground));
|
||||||
|
unsigned int bg = attrs.background == -1 ? qDefaultBackground : *((unsigned int*)(&attrs.background));
|
||||||
|
|
||||||
|
cellAttrs.foreground = attrs.reverse ? bg : fg;
|
||||||
|
cellAttrs.background = attrs.reverse ? fg : bg;
|
||||||
|
cellAttrs.special = attrs.special == -1 ? qDefaultSpecial : *((unsigned int*)(&attrs.special));
|
||||||
|
|
||||||
|
[neo_vim_osx_ui highlightSet:cellAttrs];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xpc_ui_put(UI *ui __unused, uint8_t *str, size_t len) {
|
static void xpc_ui_put(UI *ui __unused, uint8_t *str, size_t len) {
|
||||||
@ -210,7 +233,7 @@ static void xpc_ui_suspend(UI *ui __unused) {
|
|||||||
//printf("suspend\n");
|
//printf("suspend\n");
|
||||||
[neo_vim_osx_ui suspend];
|
[neo_vim_osx_ui suspend];
|
||||||
|
|
||||||
OsxXpcUiData *data = ui->data;
|
XpcUiData *data = ui->data;
|
||||||
// FIXME: dunno whether we need this: copied from tui.c
|
// FIXME: dunno whether we need this: copied from tui.c
|
||||||
// kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT
|
// kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT
|
||||||
// before continuing. This is done in another callback to avoid
|
// before continuing. This is done in another callback to avoid
|
||||||
@ -236,7 +259,7 @@ static void xpc_ui_stop(UI *ui __unused) {
|
|||||||
//printf("stop\n");
|
//printf("stop\n");
|
||||||
[neo_vim_osx_ui stop];
|
[neo_vim_osx_ui stop];
|
||||||
|
|
||||||
OsxXpcUiData *data = (OsxXpcUiData *) ui->data;
|
XpcUiData *data = (XpcUiData *) ui->data;
|
||||||
data->stop = true;
|
data->stop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,10 +7,10 @@ import Cocoa
|
|||||||
|
|
||||||
class ColorUtils {
|
class ColorUtils {
|
||||||
|
|
||||||
static func colorFromCode(rgb: UInt32) -> NSColor {
|
static func colorFromCodeIgnoringAlpha(rgb: UInt32) -> NSColor {
|
||||||
let red = (CGFloat((rgb >> 16) & 0x000000FF)) / 255.0;
|
let red = (CGFloat((rgb >> 16) & 0xFF)) / 255.0;
|
||||||
let green = (CGFloat((rgb >> 8 ) & 0x000000FF)) / 255.0;
|
let green = (CGFloat((rgb >> 8) & 0xFF)) / 255.0;
|
||||||
let blue = (CGFloat(rgb & 0x000000FF)) / 255.0;
|
let blue = (CGFloat((rgb ) & 0xFF)) / 255.0;
|
||||||
|
|
||||||
return NSColor(SRGBRed: red, green: green, blue: blue, alpha: 1.0)
|
return NSColor(SRGBRed: red, green: green, blue: blue, alpha: 1.0)
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import Foundation
|
|||||||
|
|
||||||
struct Cell: CustomStringConvertible {
|
struct Cell: CustomStringConvertible {
|
||||||
let string: String
|
let string: String
|
||||||
let attrs: HighlightAttributes
|
let attrs: CellAttributes
|
||||||
|
|
||||||
var description: String {
|
var description: String {
|
||||||
return self.string.characters.count > 0 ? self.string : "*"
|
return self.string.characters.count > 0 ? self.string : "*"
|
||||||
@ -63,23 +63,23 @@ struct Region: CustomStringConvertible {
|
|||||||
/// Almost a verbatim copy of ugrid.c of NeoVim
|
/// Almost a verbatim copy of ugrid.c of NeoVim
|
||||||
class Grid: CustomStringConvertible {
|
class Grid: CustomStringConvertible {
|
||||||
|
|
||||||
private let qEmptyHighlightAttributes = HighlightAttributes(
|
private let qEmptyCellAttributes = CellAttributes(
|
||||||
bold: false, underline: false, undercurl: false, italic: false,
|
fontTrait: .None,
|
||||||
reverse: false, foreground: -1, background: -1, special: -1
|
foreground: qDefaultForeground, background: qDefaultBackground, special: qDefaultSpecial
|
||||||
) // not static due to https://bugs.swift.org/browse/SR-1739
|
) // not static due to https://bugs.swift.org/browse/SR-1739
|
||||||
|
|
||||||
private(set) var region = Region.zero
|
private(set) var region = Region.zero
|
||||||
private(set) var size = Size.zero
|
private(set) var size = Size.zero
|
||||||
private(set) var position = Position.zero
|
private(set) var position = Position.zero
|
||||||
|
|
||||||
var foreground: Int32 = -1
|
var foreground = qDefaultForeground
|
||||||
var background: Int32 = -1
|
var background = qDefaultBackground
|
||||||
var special: Int32 = -1
|
var special = qDefaultSpecial
|
||||||
|
|
||||||
var attrs: HighlightAttributes = HighlightAttributes(
|
var attrs: CellAttributes = CellAttributes(
|
||||||
bold: false, underline: false, undercurl: false, italic: false,
|
fontTrait: .None,
|
||||||
reverse: false, foreground: -1, background: -1, special: -1
|
foreground: qDefaultForeground, background: qDefaultBackground, special: qDefaultSpecial
|
||||||
) // not using qEmptyHighlightAttributes because not static due to https://bugs.swift.org/browse/SR-1739
|
) // not using qEmptyCellAttributes because not static due to https://bugs.swift.org/browse/SR-1739
|
||||||
|
|
||||||
private(set) var cells: [[Cell]] = []
|
private(set) var cells: [[Cell]] = []
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ class Grid: CustomStringConvertible {
|
|||||||
self.size = size
|
self.size = size
|
||||||
self.position = Position.zero
|
self.position = Position.zero
|
||||||
|
|
||||||
let emptyRow = Array(count: size.width, repeatedValue: Cell(string: " ", attrs: qEmptyHighlightAttributes))
|
let emptyRow = Array(count: size.width, repeatedValue: Cell(string: " ", attrs: qEmptyCellAttributes))
|
||||||
self.cells = Array(count: size.height, repeatedValue: emptyRow)
|
self.cells = Array(count: size.height, repeatedValue: emptyRow)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,10 +158,8 @@ class Grid: CustomStringConvertible {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let clearedAttrs = HighlightAttributes(
|
let clearedAttrs = CellAttributes(fontTrait: .None,
|
||||||
bold: false, underline: false, undercurl: false, italic: false,
|
foreground: self.foreground, background: self.background, special: self.special)
|
||||||
reverse: false, foreground: self.foreground, background: self.background, special: self.background
|
|
||||||
)
|
|
||||||
|
|
||||||
let clearedCell = Cell(string: " ", attrs: clearedAttrs)
|
let clearedCell = Cell(string: " ", attrs: clearedAttrs)
|
||||||
let clearedRow = Array(count: region.right - region.left + 1, repeatedValue: clearedCell)
|
let clearedRow = Array(count: region.right - region.left + 1, repeatedValue: clearedCell)
|
||||||
|
@ -6,32 +6,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class NeoVim {
|
public class NeoVim {
|
||||||
|
|
||||||
enum UiEvent {
|
|
||||||
case MoveCursor(position: Position)
|
|
||||||
case Put(string: String)
|
|
||||||
case Resize(size: Size)
|
|
||||||
case SetHighlightAttributes(attrs: HighlightAttributes)
|
|
||||||
case Clear
|
|
||||||
case EolClear
|
|
||||||
case Flush
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Size {
|
|
||||||
let width: Int
|
|
||||||
let height: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Position {
|
|
||||||
var row: Int
|
|
||||||
var column: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ColorKind {
|
|
||||||
case Foreground
|
|
||||||
case Background
|
|
||||||
case Special
|
|
||||||
}
|
|
||||||
|
|
||||||
private static let qXpcName = "com.qvacua.nvox.xpc"
|
private static let qXpcName = "com.qvacua.nvox.xpc"
|
||||||
|
|
||||||
|
@ -5,11 +5,25 @@
|
|||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
// TODO: keep in sync with HlAttrs struct in ui.h
|
typedef NS_ENUM(NSUInteger, FontTrait) {
|
||||||
|
FontTraitNone = 0,
|
||||||
|
FontTraitItalic = (1 << 0),
|
||||||
|
FontTraitBold = (1 << 1),
|
||||||
|
FontTraitUnderline = (1 << 2),
|
||||||
|
FontTraitUndercurl = (1 << 3)
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool bold, underline, undercurl, italic, reverse;
|
FontTrait fontTrait;
|
||||||
int foreground, background, special;
|
|
||||||
} HighlightAttributes;
|
unsigned int foreground;
|
||||||
|
unsigned int background;
|
||||||
|
unsigned int special;
|
||||||
|
} CellAttributes;
|
||||||
|
|
||||||
|
#define qDefaultForeground 0xFF000000
|
||||||
|
#define qDefaultBackground 0xFFFFFFFF
|
||||||
|
#define qDefaultSpecial 0xFFFF0000
|
||||||
|
|
||||||
@protocol NeoVimUiBridgeProtocol <NSObject>
|
@protocol NeoVimUiBridgeProtocol <NSObject>
|
||||||
|
|
||||||
@ -50,7 +64,7 @@ typedef struct {
|
|||||||
|
|
||||||
- (void)setScrollRegionToTop:(int)top bottom:(int)bottom left:(int)left right:(int)right;
|
- (void)setScrollRegionToTop:(int)top bottom:(int)bottom left:(int)left right:(int)right;
|
||||||
- (void)scroll:(int)count;
|
- (void)scroll:(int)count;
|
||||||
- (void)highlightSet:(HighlightAttributes)attrs;
|
- (void)highlightSet:(CellAttributes)attrs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw string at the current cursor which was set by a previous cursorGotoRow:column callback.
|
* Draw string at the current cursor which was set by a previous cursorGotoRow:column callback.
|
||||||
|
@ -4,7 +4,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import RxSwift
|
|
||||||
|
func == (left: CellAttributes, right: CellAttributes) -> Bool {
|
||||||
|
if left.foreground != right.foreground { return false }
|
||||||
|
if left.fontTrait != right.fontTrait { return false }
|
||||||
|
|
||||||
|
if left.background != right.background { return false }
|
||||||
|
if left.special != right.special { return false }
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func != (left: CellAttributes, right: CellAttributes) -> Bool {
|
||||||
|
return !(left == right)
|
||||||
|
}
|
||||||
|
|
||||||
private struct RowFragment: CustomStringConvertible {
|
private struct RowFragment: CustomStringConvertible {
|
||||||
|
|
||||||
@ -16,35 +29,52 @@ private struct RowFragment: CustomStringConvertible {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct AttributedRowFragment: CustomStringConvertible {
|
||||||
|
|
||||||
|
let row: Int
|
||||||
|
let range: Range<Int>
|
||||||
|
let attrs: CellAttributes
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
return "AttributedRowFragment<\(row): \(range)\n\(attrs)>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class NeoVimView: NSView {
|
public class NeoVimView: NSView {
|
||||||
|
|
||||||
public var delegate: NeoVimViewDelegate?
|
public var delegate: NeoVimViewDelegate?
|
||||||
|
|
||||||
private let qDispatchMainQueue = dispatch_get_main_queue()
|
private let qDispatchMainQueue = dispatch_get_main_queue()
|
||||||
private let qLineGap = CGFloat(4)
|
|
||||||
|
private var font: NSFont {
|
||||||
private var foregroundColor = UInt32(0xFF000000)
|
didSet {
|
||||||
private var backgroundColor = UInt32(0xFFFFFFFF)
|
self.drawer.font = self.font
|
||||||
private var font = NSFont(name: "Menlo", size: 13)!
|
self.cellSize = self.drawer.cellSize
|
||||||
|
self.lineSpace = self.drawer.lineSpace
|
||||||
|
|
||||||
|
// FIXME: resize and redraw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private let xpc: NeoVimXpc
|
private let xpc: NeoVimXpc
|
||||||
private let drawer = TextDrawer()
|
private let drawer: TextDrawer
|
||||||
|
|
||||||
private var cellSize: CGSize = CGSizeMake(0, 0)
|
private var cellSize = CGSize.zero
|
||||||
|
private var lineSpace = CGFloat(0)
|
||||||
|
|
||||||
private let grid = Grid()
|
private let grid = Grid()
|
||||||
|
|
||||||
init(frame rect: NSRect = CGRect.zero, xpc: NeoVimXpc) {
|
init(frame rect: NSRect = CGRect.zero, xpc: NeoVimXpc) {
|
||||||
self.xpc = xpc
|
self.xpc = xpc
|
||||||
|
|
||||||
|
self.font = NSFont(name: "Menlo", size: 13)!
|
||||||
|
self.drawer = TextDrawer(font: font)
|
||||||
|
|
||||||
super.init(frame: rect)
|
super.init(frame: rect)
|
||||||
|
|
||||||
self.wantsLayer = true
|
self.wantsLayer = true
|
||||||
|
self.cellSize = self.drawer.cellSize
|
||||||
// hard-code some stuff
|
self.lineSpace = self.drawer.lineSpace
|
||||||
let attrs = [ NSFontAttributeName: self.font ]
|
|
||||||
let width = ceil(" ".sizeWithAttributes(attrs).width)
|
|
||||||
let height = ceil(self.font.ascender - self.font.descender + self.font.leading) + qLineGap
|
|
||||||
self.cellSize = CGSize(width: width, height: height)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override public func keyDown(theEvent: NSEvent) {
|
override public func keyDown(theEvent: NSEvent) {
|
||||||
@ -56,36 +86,32 @@ public class NeoVimView: NSView {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swift.print("------- DRAW")
|
|
||||||
|
|
||||||
let context = NSGraphicsContext.currentContext()!.CGContext
|
let context = NSGraphicsContext.currentContext()!.CGContext
|
||||||
|
|
||||||
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
|
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
|
||||||
CGContextSetTextDrawingMode(context, .Fill);
|
CGContextSetTextDrawingMode(context, .Fill);
|
||||||
|
|
||||||
let dirtyRects = self.rectsBeingDrawn()
|
let dirtyRects = self.rectsBeingDrawn()
|
||||||
self.rowFragmentsIntersecting(rects: dirtyRects).forEach { rowFrag in
|
|
||||||
|
self.attributedRowFragmentsIntersecting(rects: dirtyRects).forEach { rowFrag in
|
||||||
let positions = rowFrag.range
|
let positions = rowFrag.range
|
||||||
// filter out the put(0, 0)s (after a wide character)
|
// filter out the put(0, 0)s (after a wide character)
|
||||||
.filter { self.grid.cells[rowFrag.row][$0].string.characters.count > 0 }
|
.filter { self.grid.cells[rowFrag.row][$0].string.characters.count > 0 }
|
||||||
.map { self.positionOnView(rowFrag.row, column: $0) }
|
.map { self.positionOnView(rowFrag.row, column: $0) }
|
||||||
|
|
||||||
self.drawBackground(positions: positions)
|
|
||||||
|
|
||||||
ColorUtils.colorFromCode(self.foregroundColor).set()
|
self.drawBackground(positions: positions, background: rowFrag.attrs.background)
|
||||||
|
|
||||||
let string = self.grid.cells[rowFrag.row][rowFrag.range].reduce("") { $0 + $1.string }
|
let string = self.grid.cells[rowFrag.row][rowFrag.range].reduce("") { $0 + $1.string }
|
||||||
let glyphPositions = positions.map { CGPoint(x: $0.x, y: $0.y + qLineGap) }
|
let glyphPositions = positions.map { CGPoint(x: $0.x, y: $0.y + self.lineSpace) }
|
||||||
self.drawer.drawString(
|
self.drawer.drawString(string,
|
||||||
string, positions: UnsafeMutablePointer(glyphPositions),
|
positions: UnsafeMutablePointer(glyphPositions), positionsCount: positions.count,
|
||||||
font: self.font, foreground: self.foregroundColor, background: self.backgroundColor,
|
highlightAttrs: rowFrag.attrs,
|
||||||
context: context
|
context: context)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
// Swift.print("------- DRAW END")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func drawBackground(positions positions: [CGPoint]) {
|
private func drawBackground(positions positions: [CGPoint], background: UInt32) {
|
||||||
ColorUtils.colorFromCode(self.backgroundColor).set()
|
ColorUtils.colorFromCodeIgnoringAlpha(background).set()
|
||||||
let backgroundRect = CGRect(
|
let backgroundRect = CGRect(
|
||||||
x: positions[0].x, y: positions[0].y,
|
x: positions[0].x, y: positions[0].y,
|
||||||
width: positions.last!.x + self.cellSize.width, height: self.cellSize.height
|
width: positions.last!.x + self.cellSize.width, height: self.cellSize.height
|
||||||
@ -93,6 +119,31 @@ public class NeoVimView: NSView {
|
|||||||
backgroundRect.fill()
|
backgroundRect.fill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func attributedRowFragmentsIntersecting(rects rects: [CGRect]) -> [AttributedRowFragment] {
|
||||||
|
return self.rowFragmentsIntersecting(rects: rects)
|
||||||
|
.map { rowFrag -> [AttributedRowFragment] in
|
||||||
|
let row = rowFrag.row
|
||||||
|
let rowCells = self.grid.cells[rowFrag.row]
|
||||||
|
let range = rowFrag.range
|
||||||
|
let startIndex = range.startIndex
|
||||||
|
|
||||||
|
var result = [
|
||||||
|
AttributedRowFragment(row: row, range: startIndex...startIndex, attrs: rowCells[startIndex].attrs)
|
||||||
|
]
|
||||||
|
range.forEach { idx in
|
||||||
|
if rowCells[idx].attrs == result.last!.attrs {
|
||||||
|
let last = result.popLast()!
|
||||||
|
result.append(AttributedRowFragment(row: row, range: last.range.startIndex...idx, attrs: last.attrs))
|
||||||
|
} else {
|
||||||
|
result.append(AttributedRowFragment(row: row, range: idx...idx, attrs: rowCells[idx].attrs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
.flatMap { $0 }
|
||||||
|
}
|
||||||
|
|
||||||
private func rowFragmentsIntersecting(rects rects: [CGRect]) -> [RowFragment] {
|
private func rowFragmentsIntersecting(rects rects: [CGRect]) -> [RowFragment] {
|
||||||
return rects
|
return rects
|
||||||
.map { rect -> Region in
|
.map { rect -> Region in
|
||||||
@ -222,7 +273,7 @@ extension NeoVimView: NeoVimUiBridgeProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func highlightSet(attrs: HighlightAttributes) {
|
public func highlightSet(attrs: CellAttributes) {
|
||||||
gui {
|
gui {
|
||||||
// Swift.print("### set highlight")
|
// Swift.print("### set highlight")
|
||||||
self.grid.attrs = attrs
|
self.grid.attrs = attrs
|
||||||
@ -274,7 +325,7 @@ extension NeoVimView: NeoVimUiBridgeProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func setTitle(title: String) {
|
public func setTitle(title: String) {
|
||||||
// Swift.print("### set title: \(title)")
|
self.delegate?.setTitle(title)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setIcon(icon: String) {
|
public func setIcon(icon: String) {
|
||||||
|
@ -7,5 +7,6 @@ import Cocoa
|
|||||||
|
|
||||||
public protocol NeoVimViewDelegate {
|
public protocol NeoVimViewDelegate {
|
||||||
|
|
||||||
|
func setTitle(title: String)
|
||||||
func resizeToSize(size: CGSize)
|
func resizeToSize(size: CGSize)
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,19 @@
|
|||||||
@import Cocoa;
|
@import Cocoa;
|
||||||
@import CoreText;
|
@import CoreText;
|
||||||
|
|
||||||
|
#import "NeoVimUiBridgeProtocol.h"
|
||||||
|
|
||||||
@interface TextDrawer : NSObject
|
@interface TextDrawer : NSObject
|
||||||
|
|
||||||
- (void)drawString:(NSString *_Nonnull)theString
|
@property (nonatomic, nonnull, retain) NSFont *font;
|
||||||
positions:(CGPoint *_Nonnull)positions
|
@property (nonatomic, readonly) CGFloat lineSpace;
|
||||||
font:(NSFont *_Nonnull)font
|
@property (nonatomic, readonly) CGSize cellSize;
|
||||||
foreground:(unsigned int)foreground
|
|
||||||
background:(unsigned int)background
|
- (instancetype _Nonnull)initWithFont:(NSFont *_Nonnull)font;
|
||||||
|
|
||||||
|
- (void)drawString:(NSString *_Nonnull)string
|
||||||
|
positions:(CGPoint *_Nonnull)positions positionsCount:(NSInteger)positionsCount
|
||||||
|
highlightAttrs:(CellAttributes)attrs
|
||||||
context:(CGContextRef _Nonnull)context;
|
context:(CGContextRef _Nonnull)context;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#import "TextDrawer.h"
|
#import "TextDrawer.h"
|
||||||
#import "MMCoreTextView.h"
|
#import "MMCoreTextView.h"
|
||||||
|
#import "NeoVimUiBridgeProtocol.h"
|
||||||
|
|
||||||
#define ALPHA(color_code) (((color_code >> 24) & 0xff) / 255.0f)
|
#define ALPHA(color_code) (((color_code >> 24) & 0xff) / 255.0f)
|
||||||
#define RED(color_code) (((color_code >> 16) & 0xff) / 255.0f)
|
#define RED(color_code) (((color_code >> 16) & 0xff) / 255.0f)
|
||||||
@ -15,41 +16,95 @@
|
|||||||
#define BLUE(color_code) (((color_code ) & 0xff) / 255.0f)
|
#define BLUE(color_code) (((color_code ) & 0xff) / 255.0f)
|
||||||
|
|
||||||
@implementation TextDrawer {
|
@implementation TextDrawer {
|
||||||
NSMutableArray *fontCache;
|
NSLayoutManager *_layoutManager;
|
||||||
|
|
||||||
|
NSFont *_font;
|
||||||
|
CGFloat _fontDescent;
|
||||||
|
CGFloat _lineGap;
|
||||||
|
|
||||||
|
NSMutableArray *_fontLookupCache;
|
||||||
|
NSMutableDictionary *_fontTraitCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)init {
|
- (void)setFont:(NSFont *)font {
|
||||||
|
[_font autorelease];
|
||||||
|
_font = [font retain];
|
||||||
|
|
||||||
|
_cellSize = CGSizeMake(
|
||||||
|
round([@"m" sizeWithAttributes:@{ NSFontAttributeName : _font }].width),
|
||||||
|
[_layoutManager defaultLineHeightForFont:_font] + _lineSpace
|
||||||
|
);
|
||||||
|
// https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/FontHandling/FontHandling.html
|
||||||
|
_lineGap = _cellSize.height - _font.ascender - _font.descender;
|
||||||
|
_fontDescent = CTFontGetDescent((CTFontRef) _font);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithFont:(NSFont *_Nonnull)font {
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self == nil) {
|
if (self == nil) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
fontCache = [[NSMutableArray alloc] initWithCapacity:4];
|
_layoutManager = [[NSLayoutManager alloc] init];
|
||||||
|
_fontLookupCache = [[NSMutableArray alloc] init];
|
||||||
|
_fontTraitCache = [[NSMutableDictionary alloc] init];
|
||||||
|
|
||||||
|
_lineSpace = 4;
|
||||||
|
self.font = font;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc {
|
- (void)dealloc {
|
||||||
[fontCache release];
|
[_layoutManager release];
|
||||||
|
[_font release];
|
||||||
|
[_fontLookupCache release];
|
||||||
|
[_fontTraitCache release];
|
||||||
|
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We assume that the caller has already called
|
* We assume that the background is drawn elsewhere and that the caller has already called
|
||||||
*
|
*
|
||||||
* CGContextSetTextMatrix(context, CGAffineTransformIdentity); // or some other matrix
|
* CGContextSetTextMatrix(context, CGAffineTransformIdentity); // or some other matrix
|
||||||
* CGContextSetTextDrawingMode(context, kCGTextFill); // or some other mode
|
* CGContextSetTextDrawingMode(context, kCGTextFill); // or some other mode
|
||||||
*/
|
*/
|
||||||
- (void)drawString:(NSString *_Nonnull)theString
|
- (void)drawString:(NSString *_Nonnull)string
|
||||||
positions:(CGPoint *_Nonnull)positions
|
positions:(CGPoint *_Nonnull)positions
|
||||||
font:(NSFont *_Nonnull)theFont
|
positionsCount:(NSInteger)positionsCount
|
||||||
foreground:(unsigned int)foreground
|
highlightAttrs:(CellAttributes)attrs
|
||||||
background:(unsigned int)background
|
|
||||||
context:(CGContextRef _Nonnull)context
|
context:(CGContextRef _Nonnull)context
|
||||||
{
|
{
|
||||||
CFStringRef string = (CFStringRef) theString;
|
CGContextSaveGState(context);
|
||||||
CTFontRef font = (CTFontRef) theFont;
|
|
||||||
|
if (attrs.fontTrait & FontTraitUnderline) {
|
||||||
|
CGRect rect = {
|
||||||
|
{positions[0].x, positions[0].y - 1},
|
||||||
|
{positions[0].x + positions[positionsCount - 1].x + _cellSize.width, 1}
|
||||||
|
};
|
||||||
|
[self drawUnderline:rect color:attrs.special context:context];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self drawString:string positions:positions
|
||||||
|
fontTrait:attrs.fontTrait foreground:attrs.foreground
|
||||||
|
context:context];
|
||||||
|
|
||||||
|
CGContextRestoreGState(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)drawUnderline:(CGRect)rect color:(unsigned int)color context:(CGContextRef _Nonnull)context {
|
||||||
|
CGContextSetRGBFillColor(context, RED(color), GREEN(color), BLUE(color), ALPHA(color));
|
||||||
|
CGContextFillRect(context, rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)drawString:(NSString *_Nonnull)nsstring
|
||||||
|
positions:(CGPoint *_Nonnull)positions
|
||||||
|
fontTrait:(FontTrait)fontTrait
|
||||||
|
foreground:(unsigned int)foreground
|
||||||
|
context:(CGContextRef _Nonnull)context
|
||||||
|
{
|
||||||
|
CFStringRef string = (CFStringRef) nsstring;
|
||||||
|
|
||||||
UniChar *unibuffer = NULL;
|
UniChar *unibuffer = NULL;
|
||||||
UniCharCount unilength = (UniCharCount) CFStringGetLength(string);
|
UniCharCount unilength = (UniCharCount) CFStringGetLength(string);
|
||||||
@ -61,14 +116,52 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
CGGlyph *glyphs = malloc(unilength * sizeof(UniChar));
|
CGGlyph *glyphs = malloc(unilength * sizeof(UniChar));
|
||||||
|
CTFontRef fontWithTraits = [self fontWithTrait:fontTrait];
|
||||||
|
|
||||||
recurseDraw(unichars, glyphs, positions, unilength, context, font, fontCache, YES);
|
CGContextSetRGBFillColor(context, RED(foreground), GREEN(foreground), BLUE(foreground), 1.0);
|
||||||
|
recurseDraw(unichars, glyphs, positions, unilength, context, fontWithTraits, _fontLookupCache, YES);
|
||||||
|
|
||||||
|
CFRelease(fontWithTraits);
|
||||||
|
free(glyphs);
|
||||||
if (unibuffer != NULL) {
|
if (unibuffer != NULL) {
|
||||||
free(unibuffer);
|
free(unibuffer);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
free(glyphs);
|
/**
|
||||||
|
* The caller _must_ CFRelease the returned CTFont!
|
||||||
|
*/
|
||||||
|
- (CTFontRef)fontWithTrait:(FontTrait)fontTrait {
|
||||||
|
if (fontTrait == FontTraitNone) {
|
||||||
|
return CFRetain(_font);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTFontSymbolicTraits traits = (CTFontSymbolicTraits) 0;
|
||||||
|
if (fontTrait & FontTraitBold) {
|
||||||
|
traits |= kCTFontBoldTrait;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fontTrait & FontTraitItalic) {
|
||||||
|
traits |= kCTFontItalicTrait;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSFont *cachedFont = _fontTraitCache[@(traits)];
|
||||||
|
if (cachedFont != nil) {
|
||||||
|
return CFRetain(cachedFont);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (traits == 0) {
|
||||||
|
return CFRetain(_font);
|
||||||
|
}
|
||||||
|
|
||||||
|
CTFontRef fontWithTraits = CTFontCreateCopyWithSymbolicTraits((CTFontRef) _font, 0.0, NULL, traits, traits);
|
||||||
|
if (fontWithTraits == NULL) {
|
||||||
|
return CFRetain(_font);
|
||||||
|
}
|
||||||
|
|
||||||
|
_fontTraitCache[@(traits)] = (NSFont *) fontWithTraits;
|
||||||
|
|
||||||
|
return fontWithTraits;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -40,4 +40,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NeoVimViewDelegate {
|
|||||||
func resizeToSize(size: CGSize) {
|
func resizeToSize(size: CGSize) {
|
||||||
self.neoVim.view.setFrameSize(size)
|
self.neoVim.view.setFrameSize(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setTitle(title: String) {
|
||||||
|
self.window.title = title
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user