├── .gitignore ├── .npmignore ├── spec ├── support │ └── jasmine.json └── binding.spec.js ├── package.json ├── src ├── scrollbar-style-observer.h ├── scrollbar-style-observer-non-mac.cc └── scrollbar-style-observer-mac.mm ├── .github └── workflows │ └── pull-request.yml ├── lib └── binding.js ├── README.md ├── LICENSE.md └── binding.gyp /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build 3 | npm-debug.log 4 | .node-version 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | build/ 2 | spec/ 3 | *.coffee 4 | .travis.yml 5 | .npmignore 6 | npm-debug.log 7 | .node-version 8 | -------------------------------------------------------------------------------- /spec/support/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "spec", 3 | "spec_files": [ 4 | "**/*[sS]pec.js" 5 | ], 6 | "helpers": [ 7 | "helpers/**/*.js" 8 | ], 9 | "stopSpecOnExpectationFailure": false, 10 | "random": true 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "lib/binding.js", 3 | "dependencies": { 4 | "event-kit": "^2.5.3", 5 | "node-addon-api": "^1.1.0" 6 | }, 7 | "scripts": { 8 | "test": "jasmine" 9 | }, 10 | "gypfile": true, 11 | "name": "scrollbar-style", 12 | "version": "4.0.1", 13 | "author": "", 14 | "license": "ISC", 15 | "description": "", 16 | "devDependencies": { 17 | "jasmine": "^3.7.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/scrollbar-style-observer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class ScrollbarStyleObserver : public Napi::ObjectWrap 7 | { 8 | public: 9 | ScrollbarStyleObserver(const Napi::CallbackInfo&); 10 | ~ScrollbarStyleObserver(); 11 | void HandleScrollbarStyleChanged(); 12 | 13 | static Napi::Function GetClass(Napi::Env); 14 | 15 | private: 16 | Napi::Value GetPreferredScrollbarStyle(const Napi::CallbackInfo&); 17 | 18 | Napi::FunctionReference callback; 19 | }; 20 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | on: push 3 | 4 | jobs: 5 | run-tests: 6 | name: Run Tests 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [macos-latest, windows-latest, ubuntu-latest] 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v2 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: 12 17 | - name: Config node-gyp 18 | if: ${{ matrix.os == 'windows-latest' }} 19 | run: | 20 | npm config set node_gyp 21 | npm config set msvs_version 2019 22 | - name: Install dependencies 23 | run: npm ci 24 | - name: Run test 25 | run: npm test 26 | -------------------------------------------------------------------------------- /lib/binding.js: -------------------------------------------------------------------------------- 1 | const {ScrollbarStyleObserver} = require('../build/Release/scrollbar-style-observer-native'); 2 | const { Emitter } = require('event-kit') 3 | 4 | const emitter = new Emitter(); 5 | const observer = new ScrollbarStyleObserver(() => emitter.emit('did-change-preferred-scrollbar-style', exports.getPreferredScrollbarStyle())); 6 | 7 | exports.getPreferredScrollbarStyle = () => observer.getPreferredScrollbarStyle(); 8 | 9 | exports.onDidChangePreferredScrollbarStyle = callback => emitter.on('did-change-preferred-scrollbar-style', callback); 10 | 11 | exports.observePreferredScrollbarStyle = (callback) => { 12 | callback(exports.getPreferredScrollbarStyle()); 13 | return exports.onDidChangePreferredScrollbarStyle(callback); 14 | }; 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##### Atom and all repositories under Atom will be archived on December 15, 2022. Learn more in our [official announcement](https://github.blog/2022-06-08-sunsetting-atom/) 2 | # Scrollbar Style [![Build Status](https://travis-ci.org/atom/scrollbar-style.svg?branch=master)](https://travis-ci.org/atom/scrollbar-style) 3 | 4 | This package detects the preferred scroller style for Atom on OS X using the 5 | `+preferredScrollerStyle` method on [`NSScroller`][ns-scroller]. For 6 | compatibility, this library always returns "legacy" on Windows and Linux. 7 | 8 | ```javascript 9 | const scrollbarStyle = require('scrollbar-style') 10 | 11 | const style = scrollbarStyle.getPreferredScrollbarStyle() 12 | console.log(style) // ==> 'legacy' or 'overlay' 13 | 14 | scrollbarStyle.onDidChangePreferredScrollbarStyle((newStyle) => { 15 | console.log('style changed', newStyle) 16 | }) 17 | ``` 18 | 19 | [ns-scroller]: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSScroller_Class/Reference/Reference.html 20 | -------------------------------------------------------------------------------- /src/scrollbar-style-observer-non-mac.cc: -------------------------------------------------------------------------------- 1 | #include "scrollbar-style-observer.h" 2 | 3 | using namespace Napi; 4 | 5 | ScrollbarStyleObserver::ScrollbarStyleObserver(const Napi::CallbackInfo& info) : ObjectWrap(info) { 6 | } 7 | 8 | ScrollbarStyleObserver::~ScrollbarStyleObserver() { 9 | } 10 | 11 | Napi::Value ScrollbarStyleObserver::GetPreferredScrollbarStyle(const Napi::CallbackInfo& info) { 12 | auto env = info.Env(); 13 | Napi::HandleScope scope(env); 14 | 15 | return Napi::String::New(env, "legacy"); 16 | } 17 | 18 | Napi::Function ScrollbarStyleObserver::GetClass(Napi::Env env) { 19 | return DefineClass(env, "ScrolstyleObserver", { 20 | ScrollbarStyleObserver::InstanceMethod("getPreferredScrollbarStyle", &ScrollbarStyleObserver::GetPreferredScrollbarStyle), 21 | }); 22 | } 23 | 24 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 25 | Napi::String name = Napi::String::New(env, "ScrollbarStyleObserver"); 26 | exports.Set(name, ScrollbarStyleObserver::GetClass(env)); 27 | return exports; 28 | } 29 | 30 | NODE_API_MODULE(addon, Init) 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 GitHub Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /spec/binding.spec.js: -------------------------------------------------------------------------------- 1 | const scrollbarStyle = require('../lib/binding.js'); 2 | 3 | describe("scrollbar-style", function() { 4 | describe("getPreferredScrollbarStyle()", () => { 5 | it("returns the preferred scrollbar style", () => { 6 | const style = scrollbarStyle.getPreferredScrollbarStyle(); 7 | expect(['legacy', 'overlay'].includes(style)).toBe(true); 8 | }); 9 | }); 10 | 11 | describe("onDidChangePreferredScrollbarStyle(callback)", () => { 12 | it("returns a disposable", () => { 13 | const disposable = scrollbarStyle.onDidChangePreferredScrollbarStyle(function() {}); 14 | return disposable.dispose(); 15 | }); 16 | }); 17 | 18 | describe("observePreferredScrollbarStyle(callback)", () => { 19 | it("calls back immediately with the style", () => { 20 | const callback = jasmine.createSpy() 21 | const disposable = scrollbarStyle.observePreferredScrollbarStyle(callback); 22 | disposable.dispose(); 23 | 24 | expect(callback).toHaveBeenCalledTimes(1); 25 | const [style] = callback.calls.argsFor(0); 26 | expect(['legacy', 'overlay'].includes(style)).toBe(true); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'scrollbar-style-observer-native', 5 | 'include_dirs': [" 2 | #import "scrollbar-style-observer.h" 3 | 4 | using namespace Napi; 5 | 6 | uv_loop_t *loop = uv_default_loop(); 7 | uv_async_t async; 8 | 9 | static void notificationHandler(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { 10 | async.data = observer; 11 | uv_async_send(&async); 12 | } 13 | 14 | static void asyncSendHandler(uv_async_t *handle) { 15 | (static_cast(handle->data))->HandleScrollbarStyleChanged(); 16 | } 17 | 18 | ScrollbarStyleObserver::ScrollbarStyleObserver(const Napi::CallbackInfo& info) : ObjectWrap(info) { 19 | auto env = info.Env(); 20 | Napi::HandleScope scope(env); 21 | 22 | callback = Napi::Persistent(info[0].As()); 23 | 24 | uv_async_init(loop, &async, asyncSendHandler); 25 | 26 | CFNotificationCenterAddObserver( 27 | CFNotificationCenterGetLocalCenter(), 28 | this, 29 | ¬ificationHandler, 30 | CFSTR("NSPreferredScrollerStyleDidChangeNotification"), 31 | NULL, 32 | CFNotificationSuspensionBehaviorDeliverImmediately 33 | ); 34 | } 35 | 36 | Napi::Value ScrollbarStyleObserver::GetPreferredScrollbarStyle(const Napi::CallbackInfo& info) { 37 | auto env = info.Env(); 38 | Napi::HandleScope scope(env); 39 | 40 | if ([NSScroller preferredScrollerStyle] == NSScrollerStyleOverlay) { 41 | return Napi::String::New(env, "overlay"); 42 | } else { 43 | return Napi::String::New(env, "legacy"); 44 | } 45 | } 46 | 47 | void ScrollbarStyleObserver::HandleScrollbarStyleChanged() { 48 | callback.Call({}); 49 | } 50 | 51 | ScrollbarStyleObserver::~ScrollbarStyleObserver() { 52 | delete &callback; 53 | } 54 | 55 | Napi::Function ScrollbarStyleObserver::GetClass(Napi::Env env) { 56 | return DefineClass(env, "ScrolstyleObserver", { 57 | ScrollbarStyleObserver::InstanceMethod("getPreferredScrollbarStyle", &ScrollbarStyleObserver::GetPreferredScrollbarStyle), 58 | }); 59 | } 60 | 61 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 62 | Napi::String name = Napi::String::New(env, "ScrollbarStyleObserver"); 63 | exports.Set(name, ScrollbarStyleObserver::GetClass(env)); 64 | return exports; 65 | } 66 | 67 | NODE_API_MODULE(addon, Init) 68 | --------------------------------------------------------------------------------