- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试实现一个下拉菜单,其中每个条目都是文本或图像。看起来,基础模型中的每一列都有一个且只有一种类型。是否仍然可以允许输入文本或图像?我的想法是使用两列,其中一列为空,然后匹配一个渲染器。我只是不知道如何。
This question's answer显示了如何在不同的上下文中将两个渲染器添加到TreeStore
的一列中。当我使用ListStore
时,我以为我可以用两个渲染器之一渲染空图像的方式来解决它。但是对于ComboBox,我遇到了一个问题,即未扩展的ComboBox的宽度仅取决于第一个渲染器(第二个渲染器的结果流出),并且即使对于空白内容,第一个渲染器仍会创建很多间距。
一些消息来源暗示,实现自己的Renderer是一个好主意,该Renderer动态地将Renderer用于Pixbuf或文本。我的问题是我在gtk-rs中使用Rust。虽然可以找到一些有关实现自己的渲染器的示例,但gtk-rs文档似乎没有此类文档。由于概念上的差异(由Traits等人建模的gtk继承),将示例转移到Rust并没有太多关于这种语言和gtk的经验并不是一件容易的事,我必须承认,我不知道从哪里开始。
非常感谢您提供任何帮助和/或有关前进方向的信息!
最佳答案
似乎最好的选择是实现一个自定义类型,以与自定义渲染器一起用作模型列。自定义类型包含一个可选的String和一个可选的Pixbuf,其中应将这两个参数之一设置为某个值。我没有使用枚举来确保与glib-types的兼容性(也许也可以通过这种方式,但是我不确定)。自定义渲染器包含Strings和Pixbufs的渲染器,并可以根据给定的自定义类型动态选择要使用的渲染器。
基本上,除了以自定义类型存储外,要添加的唯一功能是提供呈现单元并提供条目的首选大小的功能。在那些情况下,自定义类型的内容用于确定应从哪个给定的渲染器中复制有关渲染和尺寸请求功能的行为。
实现自定义类型和渲染器有点挑战,因为gtk-rs的文档并不是最好的文档,尤其是在使用渲染器的子类方面。我的代码基于subclasses中a custom model和gtk-rs example repo的示例。由于我缺乏对Rust和gtk的经验,可能有很多零件可以解决得更优雅,更一致,但是我想分享我的解决方案,以防万一遇到类似问题的人正在寻找解决方案。有关如何进行的说明:
#[macro_use]
extern crate glib;
extern crate gdk_pixbuf;
extern crate gio;
extern crate gtk;
use gio::prelude::*;
use gtk::prelude::*;
use custom_glib_string_or_pixbuf::StringOrPixbuf;
use custom_glib_string_or_pixbuf_renderer::StringOrPixbufRenderer;
use gdk_pixbuf::Pixbuf;
// Content type
mod custom_glib_string_or_pixbuf {
use gdk_pixbuf::Pixbuf;
use gio::prelude::*;
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
mod internal {
use super::*;
use std::cell::RefCell;
pub struct StringOrPixbuf {
string: RefCell<Option<String>>,
pixbuf: RefCell<Option<Pixbuf>>,
}
static PROPERTIES: [subclass::Property; 2] = [
subclass::Property("string", |name| {
glib::ParamSpec::string(name, "String", "String", None, glib::ParamFlags::READWRITE)
}),
subclass::Property("pixbuf", |name| {
glib::ParamSpec::object(
name,
"Pixbuf",
"Pixbuf",
Pixbuf::static_type(),
glib::ParamFlags::READWRITE,
)
}),
];
impl ObjectSubclass for StringOrPixbuf {
const NAME: &'static str = "StringOrPixbuf";
type ParentType = glib::Object;
type Instance = subclass::simple::InstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
fn class_init(class: &mut Self::Class) {
class.install_properties(&PROPERTIES);
}
fn new() -> Self {
Self {
string: RefCell::new(None),
pixbuf: RefCell::new(None),
}
}
}
impl ObjectImpl for StringOrPixbuf {
glib_object_impl!();
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
let prop = &PROPERTIES[id];
match *prop {
subclass::Property("string", ..) => {
self.string.replace(value.get().unwrap());
}
subclass::Property("pixbuf", ..) => {
self.pixbuf.replace(value.get().unwrap());
}
_ => panic!("Tried to set unknown property of StringOrPixbuf"),
}
}
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
let prop = &PROPERTIES[id];
match *prop {
subclass::Property("string", ..) => Ok(self.string.borrow().to_value()),
subclass::Property("pixbuf", ..) => Ok(self.pixbuf.borrow().to_value()),
_ => panic!("Tried to get unknown property of StringOrPixbuf"),
}
}
}
}
glib_wrapper! {
pub struct StringOrPixbuf(Object<subclass::simple::InstanceStruct<internal::StringOrPixbuf>,subclass::simple::ClassStruct<internal::StringOrPixbuf>, StringOrPixbufClass>);
match fn {
get_type => || internal::StringOrPixbuf::get_type().to_glib(),
}
}
impl StringOrPixbuf {
pub fn new(string: Option<String>, pixbuf: Option<Pixbuf>) -> StringOrPixbuf {
glib::Object::new(
StringOrPixbuf::static_type(),
&[("string", &string), ("pixbuf", &pixbuf)],
)
.expect("Failed to create StringOrPixbuf instance")
.downcast()
.unwrap()
}
pub fn is_string(&self) -> bool {
let string_option = self
.get_property("string")
.unwrap()
.get::<String>()
.unwrap();
let pixbuf_option = self
.get_property("pixbuf")
.unwrap()
.get::<Pixbuf>()
.unwrap();
if string_option.is_some() == pixbuf_option.is_some() {
panic!("Illegal StringOrPixbuf-state")
} else if let Some(_) = string_option {
true
} else {
false
}
}
}
}
// Renderer
mod custom_glib_string_or_pixbuf_renderer {
use custom_glib_string_or_pixbuf::StringOrPixbuf;
use gdk_pixbuf::Pixbuf;
use gio::prelude::*;
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
use gtk::prelude::*;
mod internal {
use super::*;
use std::cell::RefCell;
pub struct StringOrPixbufRenderer {
text_renderer: gtk::CellRendererText,
pixbuf_renderer: gtk::CellRendererPixbuf,
string_or_pixbuf: RefCell<Option<StringOrPixbuf>>,
}
static PROPERTIES: [subclass::Property; 1] =
[subclass::Property("string_or_pixbuf", |name| {
glib::ParamSpec::object(
name,
"string_or_pixbuf",
"string_or_pixbuf",
StringOrPixbuf::static_type(),
glib::ParamFlags::READWRITE,
)
})];
impl ObjectSubclass for StringOrPixbufRenderer {
const NAME: &'static str = "StringOrPixbufRenderer";
type ParentType = gtk::CellRenderer;
type Instance = subclass::simple::InstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
glib_object_subclass!();
fn class_init(class: &mut Self::Class) {
class.install_properties(&PROPERTIES);
}
fn new() -> Self {
Self {
text_renderer: gtk::CellRendererText::new(),
pixbuf_renderer: gtk::CellRendererPixbuf::new(),
string_or_pixbuf: RefCell::new(None),
}
}
}
impl ObjectImpl for StringOrPixbufRenderer {
glib_object_impl!();
fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
let prop = &PROPERTIES[id];
match *prop {
subclass::Property("string_or_pixbuf", ..) => {
self.string_or_pixbuf.replace(value.get().unwrap());
}
_ => panic!("Tried to set unknown property of StringOrPixbufRenderer"),
}
}
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
let prop = &PROPERTIES[id];
match *prop {
subclass::Property("string_or_pixbuf", ..) => {
Ok(self.string_or_pixbuf.borrow().to_value())
}
_ => panic!("Tried to get unknown property of StringOrPixbufRenderer"),
}
}
}
impl gtk::subclass::cell_renderer::CellRendererImpl for StringOrPixbufRenderer {
fn render<P: IsA<gtk::Widget>>(
&self,
_renderer: >k::CellRenderer,
cr: &cairo::Context,
widget: &P,
background_area: &gdk::Rectangle,
cell_area: &gdk::Rectangle,
flags: gtk::CellRendererState,
) {
self.update_renderers();
if self.is_content_string() {
self.text_renderer
.render(cr, widget, background_area, cell_area, flags);
} else {
self.pixbuf_renderer
.render(cr, widget, background_area, cell_area, flags);
}
}
fn get_preferred_width<P: IsA<gtk::Widget>>(
&self,
_renderer: >k::CellRenderer,
widget: &P,
) -> (i32, i32) {
self.update_renderers();
if self.is_content_string() {
self.text_renderer.get_preferred_width(widget)
} else {
self.pixbuf_renderer.get_preferred_width(widget)
}
}
fn get_preferred_height<P: IsA<gtk::Widget>>(
&self,
_renderer: >k::CellRenderer,
widget: &P,
) -> (i32, i32) {
self.update_renderers();
if self.is_content_string() {
self.text_renderer.get_preferred_height(widget)
} else {
self.pixbuf_renderer.get_preferred_height(widget)
}
}
}
impl StringOrPixbufRenderer {
fn is_content_string(&self) -> bool {
self.string_or_pixbuf
.borrow()
.as_ref()
.expect("No StringOrPixbuf known to StringOrPixbufRenderer")
.is_string()
}
fn update_renderers(&self) {
if self.is_content_string() {
self.text_renderer.set_property_text(Some(
&self
.string_or_pixbuf
.borrow()
.as_ref()
.unwrap()
.get_property("string")
.unwrap()
.get::<String>()
.unwrap()
.unwrap()[..],
));
} else {
self.pixbuf_renderer.set_property_pixbuf(Some(
&self
.string_or_pixbuf
.borrow()
.as_ref()
.unwrap()
.get_property("pixbuf")
.unwrap()
.get::<Pixbuf>()
.unwrap()
.unwrap(),
));
}
}
}
}
glib_wrapper! {
pub struct StringOrPixbufRenderer(
Object<subclass::simple::InstanceStruct<internal::StringOrPixbufRenderer>,
subclass::simple::ClassStruct<internal::StringOrPixbufRenderer>,
SimpleAppWindowClass>)
@extends gtk::CellRenderer;
match fn {
get_type => || internal::StringOrPixbufRenderer::get_type().to_glib(),
}
}
impl StringOrPixbufRenderer {
pub fn new() -> StringOrPixbufRenderer {
glib::Object::new(StringOrPixbufRenderer::static_type(), &[])
.expect("Failed to create StringOrPixbufRenderer instance")
.downcast::<StringOrPixbufRenderer>()
.unwrap()
}
}
}
fn main() {
let application =
gtk::Application::new(None, Default::default())
.expect("failed to initialize GTK application");
application.connect_activate(|app| {
let window = gtk::ApplicationWindow::new(app);
window.set_title("Mixed Pixbuf and String ComboBox Demo");
window.set_default_size(350, 70);
let model = gtk::ListStore::new(&[StringOrPixbuf::static_type()]);
let combo = gtk::ComboBox::with_model(&model);
let row = model.append();
let image = Pixbuf::from_file("image.png")
.unwrap()
.scale_simple(100, 70, gdk_pixbuf::InterpType::Bilinear)
.unwrap();
model.set(&row, &[0], &[&StringOrPixbuf::new(None, Some(image))]);
combo.set_active(Some(0));
let row = model.append();
model.set(
&row,
&[0],
&[&StringOrPixbuf::new(
Some(String::from("Hello World")),
None,
)],
);
let row = model.append();
let image = Pixbuf::from_file("another_image.png")
.unwrap()
.scale_simple(100, 70, gdk_pixbuf::InterpType::Bilinear)
.unwrap();
model.set(&row, &[0], &[&StringOrPixbuf::new(None, Some(image))]);
let renderer = StringOrPixbufRenderer::new();
combo.pack_start(&renderer, true);
combo.add_attribute(&renderer, "string_or_pixbuf", 0);
window.add(&combo);
window.show_all();
});
application.run(&[]);
}
关于rust - 让GtkComboBox条目为Pixbuf或String,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65031222/
如果您想使用 String.Concat() 连接 5 个或更多字符串,则它会使用 Concat(String[])。 为什么不一直使用 Concat(String[]) 而不再需要 Concat(S
今天在使用 String 时,我遇到了一种我以前不知道的行为。我无法理解内部发生的事情。 public String returnVal(){ return "5";
似乎在我所看到的任何地方,都有一些过时的版本,这些版本不再起作用。 我的问题似乎很简单。我有一个Java类,它映射到derby数据库。我正在使用注释,并且已经成功地在数据库中创建了所有其他表,但是在这
一、string::size_type() 在C++标准库类型 string ,在调用size函数求解string 对象时,返回值为size_type类型,一种类似于unsigned类型的int 数据
我正在尝试将数据保存到我的 plist 文件中,其中包含字符串数组的定义。我的plist - enter image description here 我将数据写入 plist 的代码是 -- let
我有一个带有键/值对的 JavaScript 对象,其中值是字符串数组: var errors = { "Message": ["Error #1", "Error #2"], "Em
例如,为了使用相同的函数迭代 List 和 List> ,我可以编写如下内容: import java.util.*; public class Test{ public static voi
第一个Dictionary就像 Dictionary ParentDict = new Dictionary(); ParentDict.Add("A_1", "1")
这是我的 jsp 文件: 我遇到了错误 The method replace(String, String, String) in the type Functions is not appl
我需要一些帮助。我有一个方法应该输出一个包含列表内容的 txt 文件(每行中的每个项目)。列表项是字符串数组。问题是,当我调用 string.Join 时,它返回文字字符串 "System.Strin
一位同事告诉我,使用以下方法: string url = "SomeURL"; string ext = "SomeExt"; string sub = "SomeSub"; string s
给定类: public class CategoryValuePair { String category; String value; } 还有一个方法: public
我正在尝试合并 Stream>>对象与所有 Streams 中的键一起映射到单个映射中. 例如, final Map someObject; final List>> list = someObjec
在这里使用 IDictionary 的值(value)是什么? 最佳答案 使用接口(interface)的值(value)始终相同:切换到另一个后端实现时,您不必更改客户端代码。 请考虑稍后分析您的代
我可以知道这两个字典声明之间的区别吗? var places = [String: String]() var places = [Dictionary()] 为什么当我尝试以这种方式附加声明时,只有
在 .NET 4.0 及更高版本中存在 string.IsNullOrWhiteSpace(string) 时,在检查字符串时使用 string.IsNullOrEmpty(string) 是否被视为
这个名字背后的原因是什么? SS64在 PowerShell 中解释此处的字符串如下: A here string is a single-quoted or double-quoted string
我打算离开 this 文章,尝试编写一个接受字符串和 &str 的函数,但我遇到了问题。我有以下功能: pub fn new(t_num: S) -> BigNum where S: Into {
我有一个结构为 [String: [String: String]] 的多维数组。我可以使用 for 循环到达 [String: String] 位,但我不知道如何访问主键(这个位 [String:
我正在尝试使用 sarama(管理员模式)创建主题。没有 ConfigEntries 工作正常。但我需要定义一些配置。 我设置了主题配置(这里发生了错误): tConfigs := map[s
我是一名优秀的程序员,十分优秀!