Juce实现Table自定义

news2025/7/23 13:12:23

Juce实现Table自定义

一.总体展示概及概述

在项目中Juce中TableList往往无法满足用户需求,头部和背景及背景颜色设置以及在Cell中添加自定义按钮,所以需要自己实现自定义TabelList,该示例是展示实现自定义TableList,实现自定义标题头及Item,程序员在使用过程中可以自己修改颜色皮肤,按需求实现相关功能。总体展示示例如下:

请添加图片描述

二.实现步骤

1.实现自定义TableListModel

实现TableListModel的背景绘制,绘制单元格,及重新实现单元格生成的控件函数,绘制单元格边线。

#pragma once

#include <JuceHeader.h>
#include "TabelLookAndFeel.h"
#include "TableLabel.h"
#include "TabelOperCell.h"
#include <vector>
using namespace juce;

class CustomTableModel :public juce::TableListBoxModel,public Label::Listener,public TabelOperComponent::Listener
{
public:
    struct TableItemInfo
    {
        std::string str_time_;
        std::string str_name_;
        bool b_checked = false;
        TableItemInfo(const std::string& str_time, const std::string& str_name,bool bChecked=false):str_time_(str_time), str_name_(str_name),b_checked(bChecked)
        {

        }
    };

public:
    CustomTableModel(std::vector<TableItemInfo>& items):vec_table_item_(items)
    {
    }

    ~CustomTableModel()
    {
    }

    int getNumRows() override
    {
        return vec_table_item_.size();
    }

    void paintRowBackground(juce::Graphics& g, int rowNumber, int width, int height, bool rowIsSelected) override
    {
        // 绘制行背景颜色
        juce::Colour bgColour(2,75,96);// = juce::Colour(8, 82.120);/* rowNumber % 2 ? juce::Colours::white
            //: juce::Colour(0xfff8f8f8);*/
        if (rowIsSelected)
            bgColour = Colour(127,191,127);

        g.fillAll(bgColour);

        // 绘制行底部分隔线
        //g.setColour(Colour(52,110,127));
        //g.drawLine(0, height - 1, width, height - 1);
    }

    void paintCell(juce::Graphics& g, int rowNumber, int columnId, int width, int height, bool rowIsSelected) override
    {
        //if (columnId != 1) // 非复选框列
        {
            // 设置文本样式
            g.setFont(juce::Font(14.0f));
            //g.setColour(rowIsSelected ? juce::Colours::black : juce::Colours::darkgrey);
            g.setColour(Colours::white);

            if (vec_table_item_.size() > rowNumber)
            {
                if (columnId == 1)
                {
                    g.drawText(std::to_string(rowNumber+1), juce::Rectangle<int>(0, 0, width, height),
                        juce::Justification::centred, true);
                }
                //else if (columnId == 2)
                //    g.drawText(vec_table_item_[rowNumber].str_name_.c_str(), juce::Rectangle<int>(0, 0, width, height),
                //        juce::Justification::centred, true);
                else if (columnId == 3)
                    g.drawText(vec_table_item_[rowNumber].str_time_.c_str(), juce::Rectangle<int>(0, 0, width, height),
                        juce::Justification::centred, true);
            }
        }
        // 绘制方格线
        g.setColour(Colour(52, 110, 127));
        //g.drawRect(0, 0, width, height);

        //g.drawLine(0, 0, width, 0, 2); // 绘制上边线
        g.drawLine(0, 0, 0, height, 2); // 绘制左边线
        g.drawLine(0, height, width, height, 2);// 绘制下边线
        if (columnId == 4)
            g.drawLine(width, 0, width, height, 2); // 绘制右边线
       
    }

    juce::Component* refreshComponentForCell(int rowNumber, int columnId, bool isRowSelected,
        juce::Component* existingComponentToUpdate) override
    {
        
        if (columnId == 2) // 复选框列
        {
            TabelLabel* label = static_cast<TabelLabel*>(existingComponentToUpdate);

            if (label == nullptr)
            {
                label = new TabelLabel();
                label->setRow(rowNumber);
                label->setText(vec_table_item_[rowNumber].str_name_.c_str(),dontSendNotification);
                label->setEditable(true, true, false);
                label->addListener(this);
            }

            if (rowNumber < data.size())
            {
                label->setText(data[rowNumber][columnId - 1], juce::dontSendNotification);
            }

            return label;
        }
        else if (columnId == 4)
        {
            TabelOperComponent* oper = static_cast<TabelOperComponent*>(existingComponentToUpdate);
            if (oper == nullptr)
            {
                oper = new TabelOperComponent();
                oper->setRow(rowNumber);
            }
            return oper;
        }

        return nullptr;
    }

    // 设置 TableListBox 指针
    void setTable(juce::TableListBox* tableListBox)
    {
        table = tableListBox;
    }


    // 处理标签文本更改事件
    void labelTextChanged(juce::Label* label) override
    {
        TabelLabel* label_new = static_cast<TabelLabel*>(label);

        int row = label_new->getRow();
    }
    void itemClicked(TabelOperComponent* oper, int type) override
    {
        int row = oper->getRow();
    }
private:

    juce::Array<juce::StringArray> data;
    juce::TableListBox* table = nullptr;

    std::vector<TableItemInfo>& vec_table_item_;
    
    juce::Array<bool> toggleStates;
    
    //CustomToggleLookAndFeel customToggleLookAndFeel;
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CustomTableModel)
};

2.实现TableList的标题头

继承自LookAndFeel_V4 绘制Border,绘制列及绘制背景色

#pragma once
#include <JuceHeader.h>
using namespace juce;

class CustomTableHeaderLookAndFeel : public juce::LookAndFeel_V4
{
public:
    void drawTableHeaderBackground(juce::Graphics& g, juce::TableHeaderComponent& header) override
    {
        // 绘制表头背景
        //auto color = ;
        auto color = Colours::black.fromFloatRGBA(0, 0, 0, 0.25);
        g.fillAll(color);
    }
    // 新增绘制表格边框的方法
    void drawTableBorder(juce::Graphics& g, juce::TableListBox& table)
    {
        g.setColour(juce::Colour(52, 110, 127)); // 设置边框颜色
        juce::Rectangle<int> bounds = table.getLocalBounds();

        // 绘制上边框
        g.drawLine(bounds.getX(), bounds.getY(), bounds.getRight(), bounds.getY(), 1);

        // 绘制下边框
        //g.drawLine(bounds.getX(), bounds.getBottom(), bounds.getRight(), bounds.getBottom(), 1);

        // 绘制左边框
        //g.drawLine(bounds.getX(), bounds.getY(), bounds.getX(), bounds.getBottom(), 1);

        // 绘制右边框
        //g.drawLine(bounds.getRight(), bounds.getY(), bounds.getRight(), bounds.getBottom(), 1);
    }

    void drawTableHeaderColumn(juce::Graphics& g, juce::TableHeaderComponent& header,
        const juce::String& columnName, int columnId,
        int width, int height, bool isMouseOver,
        bool isMouseDown, int columnFlags) override
    {
        {
            // 其他列的文本绘制(与item文本样式一致)
            g.setFont(juce::Font(14.0f)); // 设置与item相同的字体大小
            g.setColour(juce::Colours::white);
            g.drawText(columnName, juce::Rectangle<int>(0, 0, width, height),
                juce::Justification::centred, true);
        }
        // 绘制列的边框线
        g.setColour(Colour(52, 110, 127)); // 设置边框线颜色
        //g.drawRect(0, 0, width, height, 1); // 绘制1px宽的边框线
        g.drawLine(0, 0, width, 0, 2); // 绘制上边线
        g.drawLine(0, 0, 0, height, 2); // 绘制左边线
        g.drawLine(0, height, width, height,2);
        if(columnId == 4)
            g.drawLine(width, 0, width, height, 2); // 绘制右边线
    }
};
3.在列中实现自定义button

在对应列中实现自定义component创建,如下所示:
请添加图片描述

对应自定义component的代码示例:简单实现了三个按钮,同学可以根据自己情况修改按钮,可以增加图标增加美观

class TabelOperComponent : public juce::Component,public juce::ToggleButton::Listener
{
public:
    static const int BTN_NUM = 3;
    class JUCE_API  Listener
    {
    public:
        /** Destructor. */
        virtual ~Listener() = default;


        virtual void itemClicked(TabelOperComponent* oper,int type) = 0;

    };

    void addListener(Listener* newListener) {
        const ScopedLock sl(listenerLock);
        listeners.addIfNotAlreadyThere(newListener);
    }

    void removeListener(Listener* listenerToRemove) {
        const ScopedLock sl(listenerLock);
        listeners.removeFirstMatchingValue(listenerToRemove);
    }

    TabelOperComponent();

    ~TabelOperComponent() override {}
    void setRow(int row) { row_ = row; }
    int  getRow() { return row_; }
    void paint(juce::Graphics& g) override;

    void resized() override;

    virtual void buttonClicked(Button*) override;
private:
    int row_ = 0;
    CriticalSection listenerLock;
    Array<Listener*> listeners;

    juce::ToggleButton toggleButtons[BTN_NUM];

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(TabelOperComponent)
};
#include "TabelOperCell.h"
TabelOperComponent::TabelOperComponent()
{

    for (int i = 0; i < 3; ++i)
    {
        addAndMakeVisible(toggleButtons[i]);
    }
}
void TabelOperComponent::paint(juce::Graphics& g)
{
    //g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
    auto area = getLocalBounds().toFloat();
    g.setColour(Colour(52, 110, 127));

    int width = area.getWidth();
    int height = area.getHeight();
    g.drawLine(0, 0, 0, height, 2); 
    g.drawLine(width, 0, width, height, 2); 
    g.drawLine(0, height, width, height, 2);
}

void TabelOperComponent::resized()
{
    auto area = getLocalBounds();
    int width = (area.getWidth()-6)/3;


    for (auto& button : toggleButtons)
    {
        button.setBounds(area.removeFromLeft(width).withSizeKeepingCentre(16,16));
        area.removeFromLeft(2);
    }
}

void TabelOperComponent::buttonClicked(Button* ptr_btn)
{
    for (int i = 0; i < BTN_NUM; i++)
    {
        if (ptr_btn == &toggleButtons[i])
        {
            ScopedLock lock(listenerLock);
            for (int j = listeners.size(); --j >= 0;)
                if (auto* l = listeners[j])
                    l->itemClicked(this,i);
        }
    }
}
4.在MainCompoent中使用自定义TableList

使用Table和LookAndFeel并自定义了Item参数结构体,设置TableList的属性,并自定义添加Item条数。

请添加图片描述
请添加图片描述

请添加图片描述

示例代码如下:

#pragma once

#include <JuceHeader.h>
#include "PresetTableComponent.h"
#include "TabelLookAndFeel.h"

//==============================================================================
/*
    This component lives inside our window, and this is where you should put all
    your controls and content.
*/
class MainComponent  : public juce::Component
{
public:
    //==============================================================================
    MainComponent();
    ~MainComponent() override;

    //==============================================================================
    void paint (juce::Graphics&) override;
    void resized() override;

private:
    //==============================================================================
    // Your private member variables go here...

    juce::TableListBox table_;
    CustomTableModel* table_model_ = nullptr;

    CustomTableHeaderLookAndFeel customLookAndFeel;
    std::vector<CustomTableModel::TableItemInfo> vec_table_item_;
    //TableDemoComponent table;
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

#include "MainComponent.h"

//==============================================================================
MainComponent::MainComponent()
{
    
    //addAndMakeVisible(&table);
    table_.setLookAndFeel(&customLookAndFeel);

    table_.getHeader().addColumn("ID", 1, 10);
    table_.getHeader().addColumn("Name", 2, 10);
    table_.getHeader().addColumn("Time", 3, 10 * 4);
    table_.getHeader().addColumn("Operation", 4, 60);
    table_.getHeader().setSize(getWidth(), 32);

    table_.setColour(juce::TableListBox::backgroundColourId, Colour(2, 75, 96));

    table_.getViewport()->setScrollBarsShown(false, false);

    table_.getHeader().setPopupMenuActive(false);
    table_.getHeader().addMouseListener(this, false);

    table_.getHeader().setColumnVisible(7, false); // hide the "length" column until the user shows it

    table_.setMultipleSelectionEnabled(true);

    addAndMakeVisible(&table_);
    setSize(500, 500);
}

MainComponent::~MainComponent()
{
}

//==============================================================================
void MainComponent::paint (juce::Graphics& g)
{
    // (Our component is opaque, so we must completely fill the background with a solid colour)
    g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));

    g.setFont (juce::Font (16.0f));
    g.setColour (juce::Colours::white);
    g.drawText ("Hello World!", getLocalBounds(), juce::Justification::centred, true);
}

void MainComponent::resized()
{
    auto area = getLocalBounds();

    if (table_model_)
    {
        table_.setModel(nullptr);
        delete table_model_;
        table_model_ = nullptr;
    }
    vec_table_item_.clear();

    for (int i = 0; i < 10; i++)
    {
        vec_table_item_.emplace_back("2025-04-17 13:56:00", "Name1");
    }

    if (!table_model_)
    {
        table_model_ = new CustomTableModel(vec_table_item_);
        table_.setModel(table_model_);
        table_model_->setTable(&table_);
    }
    

    auto tab_area = area;

    int colum_with = tab_area.getWidth() / 8;

    table_.setRowHeight(32);

    table_.getHeader().setColumnWidth(1, tab_area.removeFromLeft(colum_with).getWidth()); 
    table_.getHeader().setColumnWidth(2, tab_area.removeFromLeft(colum_with).getWidth());
    table_.getHeader().setColumnWidth(3, tab_area.removeFromLeft(4 * colum_with).getWidth());
    table_.getHeader().setColumnWidth(4, tab_area.getWidth());
    table_.getHeader().setSize(getWidth(), 32);
    table_.getHeader().getProperties().set("allSelected", false);

    table_.setBounds(area);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2397889.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【后端高阶面经:架构篇】51、搜索引擎架构与排序算法:面试关键知识点全解析

一、搜索引擎核心基石&#xff1a;倒排索引技术深度解析 &#xff08;一&#xff09;倒排索引的本质与构建流程 倒排索引&#xff08;Inverted Index&#xff09;是搜索引擎实现快速检索的核心数据结构&#xff0c;与传统数据库的正向索引&#xff08;文档→关键词&#xff0…

Windows应用-音视频捕获

下载“Windows应用-音视频捕获”项目 本应用可以同时捕获4个视频源和4个音频源&#xff0c;可以监视视频源图像&#xff0c;监听音频源&#xff1b;可以将视频源图像写入MP4文件&#xff0c;将音频源写入MP3或WAV文件&#xff1b;还可以录制系统播放的声音。本应用使用MFC对话框…

【OCCT+ImGUI系列】012-Geom2d_AxisPlacement

Geom2d_AxisPlacement 教学笔记 一、类概述 Geom2d_AxisPlacement 表示二维几何空间中的一个坐标轴&#xff08;轴系&#xff09;&#xff0c;由两部分组成&#xff1a; gp_Pnt2d&#xff1a;原点&#xff08;Location&#xff09;gp_Dir2d&#xff1a;单位方向向量&#xff…

【C++高并发内存池篇】性能卷王养成记:C++ 定长内存池,让内存分配快到飞起!

&#x1f4dd;本篇摘要 在本篇将介绍C定长内存池的概念及实现问题&#xff0c;引入内存池技术&#xff0c;通过实现一个简单的定长内存池部分&#xff0c;体会奥妙所在&#xff0c;进而为之后实现整体的内存池做铺垫&#xff01; &#x1f3e0;欢迎拜访&#x1f3e0;&#xff…

mac下通过anaconda安装Python

本次分享mac下通过anaconda安装Python、Jupyter Notebook、R。 anaconda安装 点击&#x1f449;https://www.anaconda.com/download&#xff0c; 点击Mac系统安装包&#xff0c; 选择Mac芯片&#xff1a;苹果芯片 or intel芯片&#xff0c; 选择苹果芯片图形界面安装&#x…

微软PowerBI考试 PL300-Power BI 入门

Power BI 入门 上篇更新了微软PowerBI考试 PL-300学习指南&#xff0c;今天分享PowerBI入门学习内容。 简介 Microsoft Power BI 是一个完整的报表解决方案&#xff0c;通过开发工具和联机平台提供数据准备、数据可视化、分发和管理。 Power BI 可以从使用单个数据源的简单…

逻辑回归知识点

一、逻辑回归概念 逻辑回归(Logistic Regression)是一种广泛应用于分类问题的统计方法&#xff0c;尤其适用于二分类问题。 注意: 尽管名称中有"回归"二字&#xff0c;但它实际上是一种分类算法。 解决二分类的问题。 API&#xff1a;sklearn.linear_model.Logis…

Ubuntu22.04 安装 CUDA12.8

1.下载CUDA 由于我装完 Ubuntu22.04 后就自动带了最新的显卡驱动&#xff0c;就没有再去配置驱动。 先查看驱动能支持的CUDA最高版本&#xff0c;这里显示可支持到12.8。 nvidia-smi在CUDA的 说明文档 可查看CUDA对应的驱动版本要求。 在 CUDA Toolkit Archive 查找需要的 …

Android的uid~package~pid的关系

UID &#xff1a; Linux 系统级用户标识&#xff0c;Android 中每个应用安装时分配唯一 UID&#xff08;如 1000&#xff09;。 Package&#xff1a; Android 应用包名(例如android)&#xff0c;一个 UID 可关联多个 Package&#xff08;共享 UID 场景如android:sharedUserI…

20250530-C#知识:万物之父Object

C#知识&#xff1a;万物之父Object Object类&#xff08;即object&#xff09;是所有类的基类&#xff0c;这里面的方法还是需要好好了解一下。 1、Object类 是顶级父类&#xff0c;其他类默认都是Object类的子类&#xff08;自定义类也会默认继承Object类&#xff09;可以用O…

多元素纳米颗粒:开启能源催化新纪元

在能源转型的浪潮中&#xff0c;纳米催化剂正成为推动能源技术突破的关键力量。多元素纳米颗粒&#xff08;Polyelemental Nanoparticles&#xff09;凭借其独特的元素协同效应&#xff0c;展现出在能源催化领域的巨大潜力。然而&#xff0c;合成这些复杂体系的纳米颗粒面临着诸…

分布式锁优化:使用Lua脚本保证释放锁的原子性问题

分布式锁优化&#xff08;二&#xff09;&#xff1a;使用Lua脚本保证释放锁的原子性问题 &#x1f4bb;黑马视频链接&#xff1a;Lua脚本解决多条命令原子性问题 在上一章节视频实现了一个可用的Redis分布式锁&#xff0c;采用SET NX EX命令实现互斥和过期自动释放机制&…

电脑wifi显示已禁用怎么点都无法启用

一、重启路由器与电脑 有时候&#xff0c;简单的重启可以解决很多小故障。试着先断开电源让路由器休息一会儿再接通&#xff1b;对于电脑&#xff0c;则可选择重启系统看看情况是否有改善。 二、检查驱动程序 无线网卡驱动程序的问题也是导致WiFi无法启用的常见原因之一。我…

【FPGA开发】Ubuntu16.04环境下配置Vivado2018.3—附软件包

文章目录 环境介绍关键步骤记录安装虚拟机及镜像安装vivadolicense导入 环境介绍 vivado&#xff1a;2018.3 虚拟机&#xff1a;vmware 16 pro 镜像&#xff1a;Ubuntu16.04 64位 所有相关软件压缩包&#xff1a; 链接&#xff1a;https://pan.quark.cn/s/fd2730b46b20 提取码…

vue-seamless-scroll 结束从头开始,加延时后滚动

今天遇到一个大屏需求&#xff1a; 1️⃣初始进入页面停留5秒&#xff0c;然后开始滚动 2️⃣最后一条数据出现在最后一行时候暂停5秒&#xff0c;然后返回1️⃣ 依次循环&#xff0c;发现vue-seamless-scroll的方法 ScrollEnd是监测最后一条数据消失在第一行才回调&#xff…

0-EATSA-GNN:基于图节点分类师生机制的边缘感知和两阶段注意力增强图神经网络(code)

code:https://github.com/afofanah/EATSA-GNN. 文章目录 Abstract1. Introduction1.1.动态图场景1.2.EATSA-GNN框架的背景化2. Background2.1.GNN边缘感知挑战2.2.GNN的可解释性问题2.3.EATSA-GNN可解释性3. Related worksAbstract 图神经网络(GNNs)从根本上改变了我们处理和…

配置前端控制器

一、DispatcherServlet 详解 在使用 Spring MVC 框架构建 Web 应用时&#xff0c;DispatcherServlet是整个请求处理流程的核心。本文将深入解析DispatcherServlet的作用、工作原理及其在 Spring MVC 架构中的关键地位。 1.DispatcherServlet 是什么&#xff1f; DispatcherS…

lua注意事项

感觉是lua的一大坑啊&#xff0c;它还不如函数内部就局部变量呢 注意函数等内部&#xff0c;全部给加上local得了

Git的三种合并方式

在 Gitee&#xff08;码云&#xff09;中合并分支主要有三种方式&#xff1a;​普通合并&#xff08;Merge Commit&#xff09;、压缩合并&#xff08;Squash Merge&#xff09;​和变基合并&#xff08;Rebase Merge&#xff09;​。每种方式适用于不同的场景&#xff0c;各有…

从零到一:我的技术博客导航(持续更新)

作者&#xff1a;冰茶 最后更新&#xff1a;2025年6月3日 本文收录了我的C#编程学习心得与技术探索&#xff0c;将持续更新 前言 作为一名.NET开发者&#xff0c;C#语言的学习与探索一直是我技术成长的核心路径。本文集整理了我在C#学习过程中的思考与实践&#xff0c;希望能够…