Revery로 네이티브 크로스플랫폼 클립보드 관리자 구축

오늘 우리는 Revery로 크로스플랫폼 (Windows, macOS, Linux) 클립보드 관리자를 구축할 것이다.
만약 당신이 아직 Revery를 들어 본 적이 없다면, 그것은 개발자의 경험을 희생하지 않고 당신에게 진정한 현지 반응을 가져다 주기 위한 라이브러리입니다.
Revery는 Reason에 쓰여 있지만 본고는 어떠한 기술의 선험 지식도 필요하지 않습니다.
우리는 전진하는 과정에서 기본 지식을 복습할 것이다. 만약 당신이 눈을 살짝 가늘게 뜨면, Reason은 약간 JavaScript처럼 보일 것이다.

입문


나는 이미 우리가 운행을 시작할 수 있도록 minimal template for Revery 하나를 준비했기 때문에 복제부터 시작하자.
git clone [email protected]:lessp/revery-minimal-template.git revery-clipboard-manager
cd revery-clipboard-manager

종속 항목 설치


Reason Native를 사용할 때 사용하는 패키지 관리자를 esy 라고 합니다.
그것의 작업 절차는 yarn 또는 npm 등 다른 우수한 도구와 매우 비슷하며, 가장 간단한 설치 방법은 npm 부터이다.그럼, 우리 계속 합시다!
npm i -g esy@latest
현재 esy 에 지정된 의존 항목을 설치하도록 자리를 잡았습니다.
esy install
# and build them
esy build
# or we could simply do, `esy`, which would combine these steps
여기에 주의해야 할 일이 좀 있다.
우선, Revery는 skia에 의존한다. 이것은 구글 크롬, 안드로이드, Flatter 등을 지원하는 원본 그래픽 엔진이다.Skia는 매우 큰 의존항입니다. 우리는 처음부터 그것을 구축하기 때문에 (다른 의존항에서) 시간이 걸릴 수도 있고 심지어 30분 이상 걸릴 수도 있습니다.😲 그래서 비록 이것은 영원한 것처럼 들리지만, 이때 이것은 예상한 것이다.
그러나 일단 구축되면 후속 구축은 거의 즉각적이다.
그 다음으로 플랫폼에 따라 Revery에서 개발할 수 있는 추가 패키지가 필요할 수 있습니다.최신 목록을 보려면 여기에서 특정 플랫폼을 찾으십시오.Building & Installing Revery
여기까지 말하고 술 한 잔 마시고 긴장을 풀자.🙂

편집기 플러그인


시간을 절약하기 위해서, 우리는 이 강좌에서 VSCode를 소개합니다. (개인적으로 vim를 사용하지만, 설정에 관심이 있다면, 언제든지 논평을 발표해 주십시오. 제가 최선을 다해 도와드리겠습니다.)우리가 사용할 플러그인은 OCaml Platform 입니다.VSCode에 설치하면 시작할 수 있습니다.

기본 응용 프로그램 설정


따라서 모든 것이 정상적인지 확인하기 위해 package.json 에서 지정한 start 스크립트를 사용하여 프로그램을 실행할 수 있기를 바랍니다.
esy start
이것은 비슷한 창을 열 수 있을 것이다.멋있어!

코드에 뭐가 있는지 봅시다.package.json에는 다음과 같이 보이는 파일이 있습니다.
open Revery;
open Revery.UI;
open Revery.UI.Components;

let init = app => {
  let window =
    App.createWindow(
      app,
      "Our window title",
      ~createOptions=WindowCreateOptions.create(
        ~width=512,
        ~height=384,
        ()
      ),
    );

  let _update = UI.start(
    window,
    <Text text="Hello DEV.to-readers!" />
  );

  ();
};

App.start(init);
이것이 바로 기본적인 Revery 응용 프로그램을 만드는 데 필요한 모든 내용입니다.좀 더 자세히 알아보겠습니다.
맨 위에는 Revery를 위한 기본 모듈이 있습니다. 이 모듈은 이 프로그램을 만드는 데 필요한 모든 기능과 모듈을 포함하고 있습니다.모듈을 열면 모듈 자체를 언급하지 않고 모든 기호를 사용할 수 있습니다.
JavaScript에서는 기본적으로 다음과 같습니다.
import * from Revery;
그리고 우리는 src라는 함수를 계속 만들었다.이 함수 수락App.re, 잠시 후 open - 함수에서 이 함수를 제공합니다.init - 함수에서 제목, 너비, 높이를 지정하는 동시에 응용 프로그램을 보여 주는 창을 만들었습니다.

Note: If you're wondering what the ~ is for, it's a labeled argument. I find labeled arguments very handy as they allow us to pass arguments without worrying about order, and also have the benefit of documenting the meaning of the argument.
e.g.

create(~width=100, ~height=200);
create(100, 200);

생성된 창을 Revery.App.t - 함수에 전달하여 응용 프로그램을 시작합니다.

Note: If you're wondering why we're prefixing the update-variable with a _ it's to let the compiler know that we'll not be using this variable to silent the warning the compiler would otherwise have given us.


마지막으로, 우리는 App.start라고 되돌아왔습니다. 다른 프로그래밍 언어에서 init라고 볼 수 있습니다.

Note: In Reason a function always needs a return-value even in this case where we're only doing side-effects.

In JavaScript for example, a function without an explicit return-value will return undefined.


클립보드 관리자 만들기


그래서 이 점에서 우리는 운행하고 있는 것이 있다.멋있지만 솔직히 소용없으니까 바꿔봅시다.
사용자 정의 구성 요소를 만드는 것부터 시작합니다.UI.start - 정의 위에 다음을 추가합니다.
let%component clipboardManager = () => {
  let%hook (clipboardText, setClipboardText) = Hooks.state("Nothing to see here, yet.");

  <Text text=clipboardText />;
}
교체
let _update = UI.start(win, <Text text="Hello DEV.to-readers!" />);
다음과 같이 사용자 정의 구성 요소를 사용합니다.
let _update = UI.start(win, <clipboardManager />);
코드를 훑어봅시다.
우리는 우선 () 을 사용한다. 이것은 이것이 상태가 있는 구성 요소가 될 것이라는 것을 의미한다.그리고 우리는 unit 모듈에서 제공한 void 갈고리를 사용한다.
let%hook (clipboardText, setClipboardText) = Hooks.state("Nothing to see here, yet.");
이것은 React의 let init 와 유사하지만, 수조가 아니라 값과 setter가 있는 let%component 를 되돌려줍니다.state의 값을 Revery.Hooks - 요소에 전달합니다. 이 요소는 현재 우리의 하드코딩 값만 보여 줍니다. (절망하지 마세요. 1분 안에 변경할 것입니다)!
어쨌든, 만약 우리가 지금 이것을 운행한다면, 우리는 이런 것을 보아야 한다.

이것은 우리의 이전 상황과 크게 다르지 않다. 시각적으로.그러나 좋은 소식은 연결이 있는 사용자 정의 구성 요소를 만들었다는 것이다.

클립보드의 내용 캡처


클립보드의 내용에 접근하기 위해서, 우리는 SDL라는 예쁜 라이브러리를 사용할 것이다.
SDL은 키보드, 마우스 및 오디오 등 여러 시스템 API에 액세스할 수 있는 다중 플랫폼 API를 제공합니다.
Revery는 SDL 라이브러리에 바인딩을 공개합니다.autocomplete를 사용하면 클립보드와 관련된 세 가지 방법을 볼 수 있습니다.

우리는 useStatetuple, 다시 말하면 no arguments을 보고 clipboardText로 돌아갈 수 있다.
실제로 Text 이런 물건은 없지만, 우리는 항상 어떤 물건이 getText 또는 unit 이라고 표시해야 하기 때문에, 우리는 option(string) -type 을 사용하는데, 이것은 우리로 하여금 이 두 가지 상황을 처리하게 할 것이다.
예시를callnull로 업데이트하겠습니다.
let%component clipboardManager = () => {
  let%hook (clipboardText, setClipboardText) = Hooks.state("");

  switch (Sdl2.Clipboard.getText()) {
  | Some(clipboardText) =>
    setClipboardText(_previousText => clipboardText);
  | None => ()
  };

  <Text text=clipboardText />;
};
만약 우리가 지금 이 예시를 다시 운행한다면, 나에게 조금도 이상하지 않다. 나는 본문과 관련된 것을 얻었다.

만약 다른 내용을 복사하려고 시도한다면, 응용 프로그램에 반영된 변경 사항을 즉시 볼 수 있을 것입니다.그것은 우리가 계속 전화를 했기 때문이다Some(thing).초당 60프레임 정도.만약 이것이 브라우저에 있다면, "최대 호출 창고를 초과했습니다."를 볼 수 있습니다.

타이머 사용


그래서 우리의 현재 방법은 결코 좋지 않다.우리는 클립보드에서 Revery 렌더링의 가장 빠른 속도로 값을 얻고 있습니다. 이것은 좀 지나친 것 같습니다.
Revery에서 어떤 일을 계속 하는 것에 대해 우리는 몇 가지 선택이 있다.이 예에서 우리는 None - 연결을 사용할 것이다.
option - 갈고리의 앞쪽 두 파라미터를 보면 getText의 표시 파라미터setClipboardText와 리셋을 받아들일 수 있고 리셋은 우리에게 하나tick를 주고 tick를 리셋 유형으로 기대할 수 있습니다.
다음은 우리가 그것을 어떻게 사용하는지의 예입니다.
Hooks.tick(
  ~tickRate=Time.ms(100),
  (time: Time.t) => Console.log(Time.toString(time))
);
코드를 업데이트하고 ~tickRate - 갈고리를 1초에 한 번 Time.t - 코드를 사용합니다.
let%component clipboardManager = () => {
  let%hook (clipboardText, setClipboardText) = Hooks.state("");

  let handleTick = _time => {
    switch (Sdl2.Clipboard.getText()) {
    | Some(clipboardText) =>
      setClipboardText(_previousText => clipboardText);
    | None => ()
    };
  };

  let%hook () = Hooks.tick(~tickRate=Time.ms(1000), handleTick);

  <Text text=clipboardText />;
};

여러 값 표시


차갑다우리는 지금 대부분의 일을 다 준비했다.그러나 우리가 이전의 값을 볼 수 있을 때만 클립보드 관리자가 진정으로 가치가 있기 때문에 그것을 복구합시다!
우선, 우리는 Time.t - 갈고리를 텍스트 문자열을 저장하는 것이 아니라 초기 값을 빈 목록으로 설정하는 문자열 목록으로 전환합니다.
let%hook (clipboardItems, setClipboardItems) = Hooks.state([]);
그 다음으로 우리는 unit - 함수에서 약간 수정해야 한다.
let handleTick = _time => {
  switch (Sdl2.Clipboard.getText()) {
  | Some(clipboardText) =>
    let alreadyExists =
      clipboardItems
      |> List.find(~f=storedClipboardText =>
            storedClipboardText == clipboardText
          )
      |> Option.isSome;

    alreadyExists
      ? ()
      : setClipboardItems(currentItems => [clipboardText, ...currentItems]);
  | None => ()
  };
};
그럼, 이곳에 무슨 변화가 생겼습니까?
좋습니다. 목록에 없는 값만 추가하는 것에 흥미가 있기 때문에 tick - 모듈의 함수를 사용했습니다.
우리는 우선 getText 을 사용하고, 그것은 하나state로 되돌아간다.마찬가지로, 우리의 항목에 일치하는 값이 없을 수도 있기 때문에, 이 함수는 하나handleTick로 되돌아옵니다.
그러나 우리의 예에서 우리는 값에 흥미가 없고 존재값이라는 사실에만 흥미를 가지기 때문에 List-모듈의 효용 함수List.findoption(string)로 바꾸고, 최종option+Optionoption(string)-함수(PR에서 Tablecloth로 바꾸는 시간!)를 사용할 것이다.
  • 만약 그것이 존재한다면 우리는 아무것도 하지 않고 돌아간다bool.
  • 존재하지 않으면 현재 클립보드의 텍스트를 기존 항목에 추가합니다.
  • Note: The |> here is called the "pipe last-operator" because it "pipes"/passes the value into the last argument of the receiving function.


    마지막으로, 우리는 구성 요소를 업데이트해서 프로젝트 목록을 보여 줍니다.
    이제 전체 어셈블리가 다음과 같이 표시됩니다.
    let%component clipboardManager = () => {
      let%hook (clipboardItems, setClipboardItems) = Hooks.state([]);
    
      let handleTick = _time => {
        switch (Sdl2.Clipboard.getText()) {
        | Some(clipboardText) =>
          let alreadyExists =
            clipboardItems
            |> List.find(~f=storedClipboardText =>
                  storedClipboardText == clipboardText
                )
            |> Option.isSome;
    
          alreadyExists
            ? ()
            : setClipboardItems(currentItems => [clipboardText, ...currentItems]);
        | None => ()
        };
      };
    
      let%hook () = Hooks.tick(~tickRate=Time.ms(1000), handleTick);
    
      let clipBoardElements =
        clipboardItems
        |> List.map(~f=text => <Text text />)
        |> React.listToElement;
    
      <Column> clipboardElements </Column>;
    };
    
    만약 우리가 그것을 실행한다면, 이것은 내가 몇 개의 항목을 복사한 후에 얻은 결과이다.

    현재 클립보드 텍스트 설정


    좋아, 우리는 이미 매우 긴 길을 걸었다.마지막 중요한 일을 보충합시다.
    현재 클립보드 값을 텍스트로 변경하려면 항목을 클릭합니다.
    기억나요List.find - 모듈에 세 개의 함수가 있나요?Option.isSome, List.existsunit.Clipboard 우리가 추구하는 것처럼 들린다.
    우리가 비추는 hasText 줄에 getText 구성 요소를 추가하여 코드를 다음과 같이 만듭니다.
    let clipboardElements =
      clipboardItems
      |> List.map(~f=text =>
           <Clickable onClick={() => Sdl2.Clipboard.setText(text)}>
             <Text text />
           </Clickable>
         )
      |> React.listToElement;
    
    목록에서 항목을 클릭하면 클립보드가 클릭한 값으로 업데이트됩니다.
    이게 다야!

    최종 코드


    이것은 우리의 결말이다.
    let%component clipboardManager = () => {
      let%hook (clipboardItems, setClipboardItems) = Hooks.state([]);
    
      let handleTick = _time => {
        switch (Sdl2.Clipboard.getText()) {
        | Some(clipboardText) =>
          let alreadyExists =
            clipboardItems
            |> List.find(~f=storedClipboardText =>
                 storedClipboardText == clipboardText
               )
            |> Option.isSome;
    
          alreadyExists
            ? ()
            : setClipboardItems(currentItems => [clipboardText, ...currentItems]);
        | None => ()
        };
      };
    
      let%hook () = Hooks.tick(~tickRate=Time.ms(1000), handleTick);
    
      let clipboardElements =
        clipboardItems
        |> List.map(~f=text =>
             <Clickable onClick={() => Sdl2.Clipboard.setText(text)}>
               <Text text />
             </Clickable>
           )
        |> React.listToElement;
    
      <Column> clipboardElements </Column>;
    };
    

    마지막 한마디


    여기까지 와줘서 고마워요. 재미있었으면 좋겠어요.
    《꿈》은 매우 멋진 프로젝트이다. 비록 그것은 매우 새롭지만, 나는 그것이 매우 큰 잠재력을 가지고 있다고 생각한다.
    만약 누군가가 흥미를 느낀다면, 우리는 두 번째 부분을 만들어서 그것을 더욱 제품처럼 만들어 볼 수 있다.
    말할 것도 없고, 모험을 좋아하고, 자신의 촉감을 높이고 싶다면, 나는 네가 어떤 생각을 가지고 있는지 보고 싶다!
    즐거운 인코딩!

    특히 본문에 대한 피드백GlennBryan에 감사드립니다.
    PSS.만약 당신이 문제가 있거나 단지 놀러 나가고 싶다면, 리버리https://discord.gg/UvQ2cFn는 의견 차이가 있을 것입니다. 당연히 가입을 환영합니다!

    좋은 웹페이지 즐겨찾기