Clojure中的JavaFX字体对话框 [英] JavaFX font face dialog in Clojure

查看:192
本文介绍了Clojure中的JavaFX字体对话框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个包含选项对话框的Clojure程序,用户可以在其中选择编辑器中使用的字体。就像许多其他程序一样,我想向用户呈现一个ComboBox,其中下拉列表显示字体本身的字体名称(例如,'Calibri'以Calibri字体显示,'Arial'以Arial显示,等等on。)

I'm building a Clojure program that includes an option dialog where the user can select the font used in an editor. Just like many other programs, I would like to present the user with a ComboBox where the dropdown list displays the font names in the font itself (e.g. 'Calibri' is displayed in the Calibri font, 'Arial' is displayed in Arial, and so on.)

在过去的Java中,我使用了一个单元工厂来自定义列表中每个单元格的外观。

In the past in Java, I have used a cell factory to customize the appearance of each cell in the list.

我对Clojure的翻译不起作用。

My translation into Clojure is not working though.

这是我到目前为止所提出的:

Here is what I have come up with so far:

(defn build-font-list-cell
"Return a Cell with an overridden updateItem implementation for 
the cells in the font list combo. Format the name of the font in
the actual font."
[]
(proxy [TextFieldListCell] []
  (updateItem [^String family mt]
    (proxy-super updateItem family mt)
    (if mt
      (.setText this nil)
      (do
        (.setFont this (Font/font family))
        (.setText this family))))))

(defn build-font-list-cell-factory
  []
  (proxy [Callback] []
    (call [list-view]
      (build-font-list-cell))))

(defn build-font-face-combo
  "Build, configure, and return the combo box used to select the font
  face for the editor."
  []
  (let [family-list (FXCollections/observableArrayList (Font/getFamilies))
    font-face-combo (ComboBox. family-list)
    current-face @tentative-font-face]

    (.setEditable font-face-combo true)
    (.addListener (.selectedItemProperty (.getSelectionModel font-face-combo))
                  ^ChangeListener (face-combo-listener font-face-combo))
    (.setCellFactory font-face-combo (build-font-list-cell-factory))
    (select-item-in-combo font-face-combo current-face)
    font-face-combo))

编译器在 build-font-list-cell ExceptionInInitializerError c>在代理的声明中起作用。 IDE(IntelliJ)在super-proxy调用中显示有关 updateItem 参数的警告,表示无法解析。我不明白为什么不这样做,因为它没有抱怨上面一行的覆盖。

The compiler throws a ExceptionInInitializerError on this in the build-font-list-cell function at the declaration of the proxy. The IDE (IntelliJ) shows a warning about the updateItem argument in the call to 'super-proxy`, saying it cannot be resolved. I don't understand why not since it doesn't complain about the override on the line above.

这似乎是一个相对简单的Java代码翻译,但我显然遗漏了一些东西。或者这是否是正确的方法?

This seems like a relatively straightforward translation of Java code that has worked before, but I'm clearly missing something. Or is this even the right approach to take?

编辑:添加以下MCVE。它如图所示编译和运行,但当然不会格式化字体名称。尝试通过取消注释列表中的代码来创建单元工厂会产生编译器阻塞的内容。

EDIT: Adding the following MCVE. It compiles and runs as shown, but does not format the font face names of course. Attempting to create a cell factory by un-commenting the code in the listing produces something that the compiler chokes on.

(ns ffcbd.core
  (:gen-class
    :extends javafx.application.Application)
  (:import (javafx.application Application)
           (javafx.collections FXCollections)
           (javafx.scene.control ComboBox)
           (javafx.scene.control.cell TextFieldListCell)
           (javafx.scene.text Font)
           (javafx.scene.layout BorderPane)
           (javafx.scene Scene)
           (javafx.stage Stage)
           (javafx.util Callback)))

;(defn build-font-list-cell []
;  (proxy [TextFieldListCell] []
;    (updateItem [family mt]
;      (proxy-super updateItem family mt)
;      (if mt
;        (.setText this nil)
;        (do
;          (.setFont this (Font/font family))
;          (.setText this family))))))

;(defn build-font-list-cell-factory []
;  (proxy [Callback] []
;    (call [list-view]
;      (build-font-list-cell))))

(defn build-font-face-combo []
  (let [family-list (FXCollections/observableArrayList (Font/getFamilies))
        font-face-combo (ComboBox. family-list)]

;    (.setCellFactory font-face-combo (build-font-list-cell-factory))
    (.select (.getSelectionModel font-face-combo) 0)
    font-face-combo))

(defn -start [this stage]
  (let [root (BorderPane.)
        scene (Scene. root)]

    (.setTop root (build-font-face-combo))
    (.add (.getChildren root) (build-font-face-combo))
    (.setMinSize root 300 275)

    (doto stage
      (.setScene scene)
      (.setTitle "Font Face ComboBox Demo")
      (.show))))

(defn -main [& args]
  (Application/launch ffcbd.core args))

另一个与Java版本的区别在于Java中的列表单元格是一个的ListCell 。但我需要调用 super.updateItem 。据我了解文档, proxy 不允许你调用super,除非方法是 public 。它是受保护的 ListCell 中,但是 public in TextFieldListCell

Another difference from the Java version is that the list cell in Java is a ListCell. But I need to call super.updateItem. As I understand the docs, proxy does not allow you to call super unless the method is public. It is protected in ListCell, but public in TextFieldListCell.

编辑#2 :以下是我一直参考的Java代码示例。

EDIT #2: Here is an example of code that works in Java that I keep referring to.

package FontFaceDialog;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.scene.control.ComboBox;
import javafx.util.Callback;

public class Main extends Application {

    private ComboBox<String> buildFontFaceCombo() {
        ObservableList<String> lst = FXCollections.observableList(javafx.scene.text.Font.getFamilies());
        ComboBox<String> cb = new ComboBox<String>(lst);
        cb.getSelectionModel().select(0);
        cb.setCellFactory((new Callback<ListView<String>, ListCell<String>>() {
            @Override
            public ListCell<String> call(ListView<String> listview) {
                return new ListCell<String>() {
                    @Override
                    protected void updateItem(String family, boolean empty) {
                        super.updateItem(family, empty);
                        if (empty) {
                            setText(null);
                        } else {
                            setFont(Font.font(family));
                            setText(family);
                        }
                    }
                };
            }
        }));
        return cb;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane root = new BorderPane();
        root.setTop(buildFontFaceCombo());
        primaryStage.setTitle("Font Face Dialog Example");
        primaryStage.setScene(new Scene(root, 300, 275));
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}


推荐答案

不幸的是到目前为止,我一直无法弄清楚如何在Clojure中做到这一点。由于我需要让这个工作,我正在使用另一种选择。既然我知道如何用Java做到这一点,为什么不只是用Java中的神秘位呢?这不是美学上令人愉悦的,但它确实有效。

Unfortunately, I have been unable to figure out how to do this in Clojure so far. Since I need to get this working, I'm using another alternative. Since I know how to do it in Java, why not just do the mystery bits in Java? It's not as aesthetically pleasing, but it works.

使用 lein 中的多语言程序是可能的,但有点可爱。

Working with polyglot programs in lein is possible but kind of fiddly.

首先,这是演示的Clojure部分。

First, here is the Clojure part of the demo.

ns ffcbd.core
  (:gen-class
    :extends javafx.application.Application)
  (:import (com.example FontFaceListCell)
           (javafx.application Application)
           (javafx.collections FXCollections)
           (javafx.scene.control ComboBox)
           (javafx.scene.text Font)
           (javafx.scene.layout BorderPane)
           (javafx.scene Scene)
           (javafx.stage Stage)
           (javafx.util Callback)))

(defn build-font-list-cell-factory []
  (proxy [Callback] []
    (call [list-view]
      (FontFaceListCell.))))

(defn build-font-face-combo []
  (let [family-list (FXCollections/observableArrayList (Font/getFamilies))
        font-face-combo (ComboBox. family-list)]
    (.setCellFactory font-face-combo (build-font-list-cell-factory))
    (.select (.getSelectionModel font-face-combo) 0)
    font-face-combo))

(defn -start [this stage]
  (let [root (BorderPane.)
        scene (Scene. root)]

    (.setTop root (build-font-face-combo))
    (.add (.getChildren root) (build-font-face-combo))
    (.setMinSize root 400 275)

    (doto stage
      (.setScene scene)
      (.setTitle "Font Face ComboBox Demo")
      (.show))))

(defn -main [& args]
  (Application/launch ffcbd.core args))

注意在顶部附近导入 com.example.FontFaceListCell 。那是Java的一部分。这是小班的列表。

Notice the import of com.example.FontFaceListCell near the top. That's the Java part. Here's the listing of that little class.

package com.example;

import javafx.scene.control.ListCell;
import javafx.scene.text.Font;

public class FontFaceListCell extends ListCell<String> {
    @Override
    public void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        if (empty) {
            setText(null);
        } else {
            setFont(Font.font(item, 16.0d));
            setText(item);
        }
    }
}

此类扩展 ListCell 并覆盖 updateItem 方法。当你运行程序并单击 ComboBox 时,我的系统会出现类似的情况。

This class extends ListCell and overrides the updateItem method. When you run the program and click on the ComboBox, I get something like this on my system.

如上所述,要使用 lein ,你需要一些更多的小东西。

As mentioned above, to get this working with lein, you need a few more fiddly bits.

为了编译Java代码, lein 需要知道Java的位置。我将此行添加到我的全局配置文件中。

In order to compile Java code, lein needs to know where Java is. I added this line to my global profile.

:java-cmd "C:\\Program Files\\Java\\jdk1.8.0_121\\bin\\java.exe"

这有点糟糕,因为现在我不能在Windows和Linux上使用相同的 profiles.clj 文件。

This kinda sucks because now I can't use the same profiles.clj file on Windows and Linux.

要使其工作还需要对 project.clj 进行一些更改,以告诉 lein Java代码所在的位置以及传递给编译器的选项。这是我使用的。

Getting this to work also requires a few changes to the project.clj to tell lein where the Java code is and which options to pass to the compiler. Here is what I used.

(defproject ffcbd "0.1.0-SNAPSHOT"
  :description "A demo of a styled font selection ComboBox in Clojure."
  :dependencies [[org.clojure/clojure "1.8.0"]]
  :java-source-paths ["java"]
  :javac-options     ["-target" "1.8" "-source" "1.8"]
  :aot :all
  :main ffcbd.core)

我已经提出了一个Mercurial repo ,包括一个感兴趣的任何人的IntelliJ IDEA项目。

I've put up a Mercurial repo on Bitbucket including an IntelliJ IDEA project for anyone interested.

使用Clojure让所有这些工作仍然很好虽然。我确定 gen-class 会这样做,但还没想出来。

It would still be nice to get all of this working only using Clojure though. I'm sure gen-class would do it, but haven't figured it out yet.

这篇关于Clojure中的JavaFX字体对话框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆