diff --git a/CMakeLists.txt b/CMakeLists.txt index 64048fbe0..2d1df9e05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,7 @@ add_library(${CORE_WIDGETS_ADDON} SHARED "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QEvent/QDragMoveEvent/qdragmoveevent_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QEvent/QDragLeaveEvent/qdragleaveevent_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QFontDatabase/qfontdatabase_wrap.cpp" + "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QFontMetrics/qfontmetrics_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QPicture/qpicture_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QPixmap/qpixmap_wrap.cpp" "${PROJECT_SOURCE_DIR}/src/cpp/lib/QtGui/QIcon/qicon_wrap.cpp" @@ -220,7 +221,7 @@ target_include_directories(${CORE_WIDGETS_ADDON} PRIVATE ) -if (WIN32) +if (WIN32) add_definitions(/bigobj) target_compile_definitions(${CORE_WIDGETS_ADDON} PRIVATE ENABLE_DLL_EXPORT=1 @@ -236,4 +237,3 @@ target_link_libraries(${CORE_WIDGETS_ADDON} PRIVATE Qt5::Gui Qt5::Svg ) - diff --git a/src/cpp/include/nodegui/QtGui/QFontMetrics/qfontmetrics_wrap.h b/src/cpp/include/nodegui/QtGui/QFontMetrics/qfontmetrics_wrap.h new file mode 100644 index 000000000..efb4cbbf9 --- /dev/null +++ b/src/cpp/include/nodegui/QtGui/QFontMetrics/qfontmetrics_wrap.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include + +#include "Extras/Export/export.h" +#include "core/Component/component_macro.h" + +class DLL_EXPORT QFontMetricsWrap : public Napi::ObjectWrap { + COMPONENT_WRAPPED_METHODS_DECLARATION + + private: + std::unique_ptr instance; + + public: + static Napi::FunctionReference constructor; + static Napi::Object init(Napi::Env env, Napi::Object exports); + QFontMetricsWrap(const Napi::CallbackInfo& info); + QFontMetrics* getInternalInstance(); + // Wrapped methods + Napi::Value ascent(const Napi::CallbackInfo& info); + Napi::Value averageCharWidth(const Napi::CallbackInfo& info); + Napi::Value capHeight(const Napi::CallbackInfo& info); + Napi::Value descent(const Napi::CallbackInfo& info); + Napi::Value fontDpi(const Napi::CallbackInfo& info); + Napi::Value height(const Napi::CallbackInfo& info); + Napi::Value horizontalAdvance(const Napi::CallbackInfo& info); + Napi::Value inFont(const Napi::CallbackInfo& info); + Napi::Value leading(const Napi::CallbackInfo& info); + Napi::Value leftBearing(const Napi::CallbackInfo& info); + Napi::Value lineSpacing(const Napi::CallbackInfo& info); + Napi::Value lineWidth(const Napi::CallbackInfo& info); + Napi::Value overlinePos(const Napi::CallbackInfo& info); + Napi::Value rightBearing(const Napi::CallbackInfo& info); + Napi::Value size(const Napi::CallbackInfo& info); + Napi::Value strikeOutPos(const Napi::CallbackInfo& info); + Napi::Value swap(const Napi::CallbackInfo& info); + Napi::Value underlinePos(const Napi::CallbackInfo& info); +}; diff --git a/src/cpp/lib/QtGui/QFontMetrics/qfontmetrics_wrap.cpp b/src/cpp/lib/QtGui/QFontMetrics/qfontmetrics_wrap.cpp new file mode 100644 index 000000000..444c4ae51 --- /dev/null +++ b/src/cpp/lib/QtGui/QFontMetrics/qfontmetrics_wrap.cpp @@ -0,0 +1,221 @@ +#include "QtGui/QFontMetrics/qfontmetrics_wrap.h" + +#include "Extras/Utils/nutils.h" +#include "QtCore/QSize/qsize_wrap.h" +#include "QtGui/QFont/qfont_wrap.h" + +Napi::FunctionReference QFontMetricsWrap::constructor; + +Napi::Object QFontMetricsWrap::init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); + char CLASSNAME[] = "QFontMetrics"; + Napi::Function func = DefineClass( + env, CLASSNAME, + {InstanceMethod("ascent", &QFontMetricsWrap::ascent), + InstanceMethod("averageCharWidth", &QFontMetricsWrap::averageCharWidth), + InstanceMethod("capHeight", &QFontMetricsWrap::capHeight), + InstanceMethod("descent", &QFontMetricsWrap::descent), + InstanceMethod("fontDpi", &QFontMetricsWrap::fontDpi), + InstanceMethod("height", &QFontMetricsWrap::height), + InstanceMethod("horizontalAdvance", + &QFontMetricsWrap::horizontalAdvance), + InstanceMethod("inFont", &QFontMetricsWrap::inFont), + InstanceMethod("leading", &QFontMetricsWrap::leading), + InstanceMethod("leftBearing", &QFontMetricsWrap::leftBearing), + InstanceMethod("lineSpacing", &QFontMetricsWrap::lineSpacing), + InstanceMethod("lineWidth", &QFontMetricsWrap::lineWidth), + InstanceMethod("overlinePos", &QFontMetricsWrap::overlinePos), + InstanceMethod("rightBearing", &QFontMetricsWrap::rightBearing), + InstanceMethod("size", &QFontMetricsWrap::size), + InstanceMethod("strikeOutPos", &QFontMetricsWrap::strikeOutPos), + InstanceMethod("swap", &QFontMetricsWrap::swap), + InstanceMethod("underlinePos", &QFontMetricsWrap::underlinePos), + COMPONENT_WRAPPED_METHODS_EXPORT_DEFINE(QFontMetricsWrap)}); + constructor = Napi::Persistent(func); + exports.Set(CLASSNAME, func); + return exports; +} + +QFontMetricsWrap::QFontMetricsWrap(const Napi::CallbackInfo& info) + : Napi::ObjectWrap(info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + if (info.Length() == 1) { + if (info[0].IsExternal()) { + this->instance = std::unique_ptr( + info[0].As>().Data()); + } else { + Napi::Object wrap0_0 = info[0].As(); + QFontWrap* wrap0_1 = Napi::ObjectWrap::Unwrap(wrap0_0); + this->instance = + std::make_unique(*wrap0_1->getInternalInstance()); + } + } else { + Napi::TypeError::New(env, "Wrong number of arguments") + .ThrowAsJavaScriptException(); + } + this->rawData = extrautils::configureComponent(this->getInternalInstance()); +} + +QFontMetrics* QFontMetricsWrap::getInternalInstance() { + return this->instance.get(); +} + +Napi::Value QFontMetricsWrap::ascent(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + return Napi::Value::From(env, this->instance->ascent()); +} + +Napi::Value QFontMetricsWrap::averageCharWidth(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + return Napi::Value::From(env, this->instance->averageCharWidth()); +} + +Napi::Value QFontMetricsWrap::capHeight(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + return Napi::Value::From(env, this->instance->capHeight()); +} + +Napi::Value QFontMetricsWrap::descent(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + return Napi::Value::From(env, this->instance->descent()); +} + +Napi::Value QFontMetricsWrap::fontDpi(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + return Napi::Value::From(env, this->instance->fontDpi()); +} + +Napi::Value QFontMetricsWrap::height(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + return Napi::Value::From(env, this->instance->height()); +} + +Napi::Value QFontMetricsWrap::horizontalAdvance( + const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + std::string format = info[0].As().Utf8Value(); + QString text = QString::fromUtf8(format.c_str()); + if (info.Length() <= 2 && text.length() == 1) { + return Napi::Value::From(env, this->instance->horizontalAdvance(text[0])); + } else if (info.Length() == 2) { + int len = info[1].As().Int32Value(); + return Napi::Value::From(env, this->instance->horizontalAdvance(text, len)); + } else { + Napi::TypeError::New(env, + "Invalid number of arguments to horizontalAdvance") + .ThrowAsJavaScriptException(); + return env.Null(); + } +} + +Napi::Value QFontMetricsWrap::inFont(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + char ch = info[0].As().Utf8Value()[0]; + return Napi::Boolean::New(env, this->instance->inFont(ch)); +} + +Napi::Value QFontMetricsWrap::leading(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + return Napi::Value::From(env, this->instance->leading()); +} + +Napi::Value QFontMetricsWrap::leftBearing(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + char ch = info[0].As().Utf8Value()[0]; + return Napi::Boolean::New(env, this->instance->leftBearing(ch)); +} + +Napi::Value QFontMetricsWrap::lineSpacing(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + return Napi::Value::From(env, this->instance->lineSpacing()); +} + +Napi::Value QFontMetricsWrap::lineWidth(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + return Napi::Value::From(env, this->instance->lineWidth()); +} + +Napi::Value QFontMetricsWrap::overlinePos(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + return Napi::Value::From(env, this->instance->overlinePos()); +} + +Napi::Value QFontMetricsWrap::rightBearing(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + char ch = info[0].As().Utf8Value()[0]; + return Napi::Boolean::New(env, this->instance->rightBearing(ch)); +} + +Napi::Value QFontMetricsWrap::size(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + if (info.Length() != 2 && info.Length() != 3) { + Napi::TypeError::New(env, "Invalid number of arguments to size") + .ThrowAsJavaScriptException(); + return env.Null(); + } + int flags = info[0].As().Int32Value(); + std::string format = info[1].As().Utf8Value(); + QString text = QString::fromUtf8(format.c_str()); + int tabStops = info[2].As().Int32Value(); + QSize size = this->instance->size(flags, text, tabStops); + auto instance = QSizeWrap::constructor.New( + {Napi::External::New(env, new QSize(size))}); + return instance; +} + +Napi::Value QFontMetricsWrap::strikeOutPos(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + return Napi::Value::From(env, this->instance->strikeOutPos()); +} + +Napi::Value QFontMetricsWrap::swap(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + Napi::Object wrap0_0 = info[0].As(); + QFontMetricsWrap* wrap0_1 = + Napi::ObjectWrap::Unwrap(wrap0_0); + this->instance->swap(*wrap0_1->getInternalInstance()); + return env.Null(); +} + +Napi::Value QFontMetricsWrap::underlinePos(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); + + return Napi::Value::From(env, this->instance->underlinePos()); +} diff --git a/src/cpp/main.cpp b/src/cpp/main.cpp index 5e93903b9..976333bae 100644 --- a/src/cpp/main.cpp +++ b/src/cpp/main.cpp @@ -31,6 +31,7 @@ #include "QtGui/QEvent/QWheelEvent/qwheelevent_wrap.h" #include "QtGui/QFont/qfont_wrap.h" #include "QtGui/QFontDatabase/qfontdatabase_wrap.h" +#include "QtGui/QFontMetrics/qfontmetrics_wrap.h" #include "QtGui/QIcon/qicon_wrap.h" #include "QtGui/QImage/qimage_wrap.h" #include "QtGui/QKeySequence/qkeysequence_wrap.h" @@ -137,6 +138,7 @@ Napi::Object Main(Napi::Env env, Napi::Object exports) { QPixmapWrap::init(env, exports); QKeySequenceWrap::init(env, exports); QFontDatabaseWrap::init(env, exports); + QFontMetricsWrap::init(env, exports); QIconWrap::init(env, exports); QImageWrap::init(env, exports); QFontWrap::init(env, exports); @@ -217,4 +219,4 @@ Napi::Object Main(Napi::Env env, Napi::Object exports) { return exports; } -NODE_API_MODULE(NODE_GYP_MODULE_NAME, Main) \ No newline at end of file +NODE_API_MODULE(NODE_GYP_MODULE_NAME, Main) diff --git a/src/index.ts b/src/index.ts index 9add0b634..666f20a6d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,6 +18,7 @@ export { QTextOptionWrapMode } from './lib/QtGui/QTextOption'; export { QClipboard, QClipboardMode } from './lib/QtGui/QClipboard'; export { QStyle, QStylePixelMetric } from './lib/QtGui/QStyle'; export { QFontDatabase, SystemFont, WritingSystem } from './lib/QtGui/QFontDatabase'; +export { QFontMetrics } from './lib/QtGui/QFontMetrics'; // Events: Maybe a separate module ? export { QKeyEvent } from './lib/QtGui/QEvent/QKeyEvent'; export { QMouseEvent } from './lib/QtGui/QEvent/QMouseEvent'; diff --git a/src/lib/QtEnums/TextFlag/index.ts b/src/lib/QtEnums/TextFlag/index.ts index 9066d11c7..0844c93dc 100644 --- a/src/lib/QtEnums/TextFlag/index.ts +++ b/src/lib/QtEnums/TextFlag/index.ts @@ -1,4 +1,5 @@ export enum TextFlag { + None = 0x0, TextSingleLine = 0x0100, TextDontClip = 0x0200, TextExpandTabs = 0x0400, diff --git a/src/lib/QtGui/QFontMetrics.ts b/src/lib/QtGui/QFontMetrics.ts new file mode 100644 index 000000000..aa4de6154 --- /dev/null +++ b/src/lib/QtGui/QFontMetrics.ts @@ -0,0 +1,118 @@ +import addon from '../utils/addon'; +import { Component, NativeElement } from '../core/Component'; +import { QFont } from './QFont'; +import { checkIfNativeElement } from '../utils/helpers'; +import { QSize } from '../QtCore/QSize'; +import { TextFlag } from '../QtEnums'; + +export class QFontMetrics extends Component { + native: NativeElement; + constructor(native: NativeElement); + constructor(qfont: QFont); + constructor(qfontmetrics: QFontMetrics); + constructor(arg: QFont | QFontMetrics | NativeElement) { + super(); + if (checkIfNativeElement(arg)) { + this.native = arg as NativeElement; + } else if (arg instanceof QFontMetrics) { + this.native = arg.native; + } else { + this.native = new addon.QFontMetrics(arg.native); + } + } + + /** Returns the ascent of the font */ + ascent(): number { + return this.native.ascent(); + } + + /** Returns the average width of glyphs in the font */ + averageCharWidth(): number { + return this.native.averageCharWidth(); + } + + /** Returns the cap height of the font */ + capHeight(): number { + return this.native.capHeight(); + } + + /** Returns the descent of the font */ + descent(): number { + return this.native.descent(); + } + + /** Returns the font DPI */ + fontDpi(): number { + return this.native.fontDpi(); + } + + /** Returns the height of the font */ + height(): number { + return this.native.height(); + } + + /** Returns the horizontal advance in pixels of the first len characters of text. If len is negative (the default), the entire string is used */ + horizontalAdvance(text: string, len = -1): number { + return this.native.horizontalAdvance(text, len); + } + + /** Returns true if character ch is a valid character in the font; otherwise returns false */ + inFont(text: string): number { + return this.native.inFont(text); + } + + /** Returns the leading of the font */ + leading(): number { + return this.native.leading(); + } + + /** Returns the left bearing of character ch in the font */ + leftBearing(text: string): number { + return this.native.leftBearing(text); + } + + /** Returns the distance from one base line to the next */ + lineSpacing(): number { + return this.native.lineSpacing(); + } + + /** Returns the width of the underline and strikeout lines, adjusted for the point size of the font */ + lineWidth(): number { + return this.native.lineWidth(); + } + + /** Returns the distance from the base line to where an overline should be drawn */ + overlinePos(): number { + return this.native.overlinePos(); + } + + /** Returns the right bearing of character ch in the font */ + rightBearing(text: string): number { + return this.native.rightBearing(text); + } + + /** + * Returns the size in pixels of text + * + * See QtEnums::TextFlag for flags + */ + size(flags: TextFlag, text: string, tabStops = 0): QSize { + const native = this.native.size(flags, text, tabStops); + return new QSize(native); + } + + /** Returns the distance from the base line to where the strikeout line should be drawn */ + strikeOutPos(): number { + return this.native.strikeOutPos(); + } + + /** Swaps metrics other with this metrics. This operation is very fast and never fails */ + swap(other: QFontMetrics): void { + return this.native.swap(other.native); + } + + /** Returns the distance from the base line to where an underscore should be drawn */ + underlinePos(): number { + return this.native.underlinePos(); + } +} diff --git a/src/lib/QtGui/__tests__/QFontMetrics.test.ts b/src/lib/QtGui/__tests__/QFontMetrics.test.ts new file mode 100644 index 000000000..42f545f24 --- /dev/null +++ b/src/lib/QtGui/__tests__/QFontMetrics.test.ts @@ -0,0 +1,106 @@ +import { TextFlag } from '../../QtEnums'; +import { QFont } from '../QFont'; +import { QFontMetrics } from '../QFontMetrics'; + +describe('QFontMetrics', () => { + // Helvetica is Qt's default sans-serif font + const qfont = new QFont('Helvetica'); + it('initialize with QFont', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics).toBeTruthy(); + expect(metrics.native).toBeTruthy(); + }); + it('averageCharWidth', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.averageCharWidth).toBeTruthy(); + expect(metrics.averageCharWidth()).not.toBeNaN(); + }); + it('capHeight', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.capHeight).toBeTruthy(); + expect(metrics.capHeight()).not.toBeNaN(); + }); + it('descent', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.descent).toBeTruthy(); + expect(metrics.descent()).not.toBeNaN(); + }); + it('fontDpi', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.fontDpi).toBeTruthy(); + expect(metrics.fontDpi()).not.toBeNaN(); + }); + it('height', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.height).toBeTruthy(); + expect(metrics.height()).not.toBeNaN(); + }); + it('horizontalAdvance', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.horizontalAdvance).toBeTruthy(); + expect(metrics.horizontalAdvance('a')).not.toBeNaN(); + expect(metrics.horizontalAdvance('aaa')).not.toBeNaN(); + }); + it('inFont', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.inFont).toBeTruthy(); + expect(metrics.inFont('a')).toBe(true); + }); + it('leading', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.leading).toBeTruthy(); + expect(metrics.leading()).not.toBeNaN(); + }); + it('leftBearing', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.leftBearing).toBeTruthy(); + expect(metrics.leftBearing('a')).not.toBeNaN(); + }); + it('lineSpacing', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.lineSpacing).toBeTruthy(); + expect(metrics.lineSpacing()).not.toBeNaN(); + }); + it('lineWidth', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.lineWidth).toBeTruthy(); + expect(metrics.lineWidth()).not.toBeNaN(); + }); + it('overlinePos', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.overlinePos).toBeTruthy(); + expect(metrics.overlinePos()).not.toBeNaN(); + }); + it('rightBearing', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.rightBearing).toBeTruthy(); + expect(metrics.rightBearing('a')).not.toBeNaN(); + }); + it('size', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.size).toBeTruthy(); + const sizeDefault = metrics.size(TextFlag.None, 'abc'); + expect(sizeDefault.height()).toBeGreaterThan(0); + expect(sizeDefault.width()).toBeGreaterThan(0); + const sizeFlag1 = metrics.size(TextFlag.TextExpandTabs, '\tabc', 1); + const sizeFlag10 = metrics.size(TextFlag.TextExpandTabs, '\tabc', 10); + expect(sizeFlag1.width()).toBeGreaterThan(sizeDefault.width()); + expect(sizeFlag10.width()).toBeGreaterThan(sizeFlag1.width()); + }); + it('strikeOutPos', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.strikeOutPos).toBeTruthy(); + expect(metrics.strikeOutPos()).not.toBeNaN(); + }); + it('swap', () => { + const metrics = new QFontMetrics(qfont); + const metrics2 = new QFontMetrics(qfont); + expect(metrics.swap).toBeTruthy(); + expect(() => metrics.swap(metrics2)).not.toThrow(); + }); + it('underlinePos', () => { + const metrics = new QFontMetrics(qfont); + expect(metrics.underlinePos).toBeTruthy(); + expect(metrics.underlinePos()).not.toBeNaN(); + }); +});